├── .gitignore ├── README.md ├── configuration ├── assets │ ├── profile.sh │ └── vimrc ├── playbook │ ├── .gitignore │ ├── all.yml │ ├── install_golang │ ├── install_wiringpi │ ├── optional │ │ └── rngtools.yml │ └── rpi.yml └── setup_pi └── roles ├── dnsmasq_adblocker ├── dnsmasq_server ├── ethernet_bridge ├── kiwix_server ├── install_kiwix └── kiwix_server.yml ├── octoprint ├── .gitignore ├── Makefile ├── README.md ├── provision.yml ├── service.systemd ├── setup_spyglass ├── setup_virtualenv └── webcam.systemd ├── ospi ├── ospi.service └── provision.yml ├── scanny ├── Makefile ├── install_drivers ├── print_server └── provision.yml └── stratum_1_timeserver ├── README.md ├── chrony.conf ├── chrony.service ├── compile_gpsd ├── gpsd.service ├── i2c_check └── provision.yml /.gitignore: -------------------------------------------------------------------------------- 1 | secrets.yml 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | raspberrypi 2 | =========== 3 | Welcome to my Raspberry Pi tools and scripts repo. 4 | 5 | The goal of this repo is to make setting up and using 6 | the raspberrry pi fun and easy. 7 | 8 | Good luck, and do cool stuff! 9 | -------------------------------------------------------------------------------- /configuration/assets/profile.sh: -------------------------------------------------------------------------------- 1 | # make less case insensitive for searches and display verbose prompt by default 2 | export LESS="-irMS" 3 | 4 | # Add terminal colors 5 | export CLICOLOR=1 6 | 7 | export FZF_DEFAULT_OPTS='--multi --no-height --extended' 8 | 9 | # Don't put duplicate lines in the history. also, don't save lines that begin 10 | # with a whitespace character 11 | export HISTCONTROL=erasedups:ignorespace 12 | export HISTTIMEFORMAT='%F %T ' 13 | export HISTSIZE=20000 14 | shopt -s histappend 15 | 16 | # Make the bash history save for all open terminals 17 | export PROMPT_COMMAND='history -a' 18 | 19 | # Collapses multi-line commands into a single history item 20 | shopt -s cmdhist 21 | 22 | # ignore these short commands in the history 23 | export HISTIGNORE="&:cd:fg:bg:pwd:pd:po:pushd:popd:dirs:ls:jobs:top" 24 | 25 | alias grp="grep -rnI" 26 | 27 | # Better ls 28 | alias ll="ls -ltr" 29 | 30 | # Format python 31 | function lint { 32 | if [[ -z "${1+x}" ]]; then 33 | echo "First argument must be a python file." 34 | return 35 | fi 36 | black --line-length=120 ${1} 37 | isort --profile=black ${1} 38 | } 39 | -------------------------------------------------------------------------------- /configuration/assets/vimrc: -------------------------------------------------------------------------------- 1 | "Don't load default vim configs 2 | let skip_defaults_vim=1 3 | 4 | set showcmd 5 | set modifiable 6 | 7 | filetype on 8 | filetype plugin on 9 | 10 | :syntax on 11 | 12 | set smartindent 13 | set statusline+=%#warningmsg# 14 | set backspace=indent,eol,start 15 | 16 | " Enable modelines (the things at the tops of files that allow you to alter VIM's 17 | " natural behavior 18 | set modelines=5 19 | 20 | set laststatus=2 21 | set t_Co=256 22 | set paste 23 | set softtabstop=4 24 | set tabstop=4 25 | set shiftwidth=4 26 | set expandtab 27 | 28 | " To get backspace work 29 | set backspace=2 30 | 31 | " Search ignore case, realtime highlight, highlight all 32 | set ignorecase 33 | set incsearch 34 | set hlsearch 35 | 36 | " Turn on line nums by default but \n turns them off 37 | set nu 38 | :nmap \n :set number! 39 | 40 | "Found at: https://github.com/tylertreat/dotfiles/blob/master/vimrc 41 | set statusline=%t "tail of the filename 42 | set statusline+=[%{strlen(&fenc)?&fenc:'none'}, "file encoding 43 | set statusline+=%{&ff}] "file format 44 | set statusline+=%h "help file flag 45 | set statusline+=%m "modified flag 46 | set statusline+=%r "read only flag 47 | set statusline+=%y "filetype 48 | set statusline+=%= "left/right separator 49 | set statusline+=%c, "cursor column 50 | set statusline+=%l/%L "cursor line/total lines 51 | set statusline+=\ %P "percent through file 52 | 53 | " Show limit line of 80 chars and highlight with blue 54 | set colorcolumn=80 55 | hi ColorColumn cterm=NONE ctermbg=17 56 | 57 | 58 | " Highlighting of the current line. Uses a dark, almost translucent black color 59 | set cul 60 | hi CursorLine cterm=NONE ctermbg=234 ctermfg=NONE 61 | 62 | " Allow comments of blocks 63 | " # -- comment block 64 | " -# -- uncomment block 65 | vnoremap # :s#^#\## 66 | vnoremap -# :s#^\### 67 | 68 | " Add pdb break points for python files. \b current line or \B line above 69 | au FileType python map b oimport pdb; pdb.set_trace() 70 | 71 | " Automatically use tabs in makefiles 72 | autocmd FileType make setlocal noexpandtab 73 | -------------------------------------------------------------------------------- /configuration/playbook/.gitignore: -------------------------------------------------------------------------------- 1 | secret.txt 2 | rpirc 3 | secret_* 4 | -------------------------------------------------------------------------------- /configuration/playbook/all.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: yes 4 | tasks: 5 | # Do this as soon as possible because it can cause http issues 6 | - name: disable ipv6 7 | ansible.posix.sysctl: 8 | name: net.ipv6.conf.all.disable_ipv6 9 | value: '1' 10 | sysctl_set: true 11 | state: present 12 | reload: true 13 | - name: Add me 14 | user: 15 | name: smw 16 | comment: Stephen Wood 17 | groups: sudo, adm 18 | shell: /bin/bash 19 | append: yes 20 | - name: Set authorized keys for smw 21 | ansible.posix.authorized_key: 22 | user: smw 23 | state: present 24 | key: https://github.com/stephen-mw.keys 25 | - name: install packages 26 | apt: 27 | update_cache: true 28 | state: latest 29 | name: 30 | - chrony 31 | - curl 32 | - fzf 33 | - git 34 | - jq 35 | - lsof 36 | - mosh 37 | - net-tools 38 | - python3-pip 39 | - python3-virtualenv 40 | - rng-tools 41 | - rsyslog 42 | - screen 43 | - software-properties-common 44 | - ssh-import-id 45 | - unp 46 | - vim 47 | - wget 48 | - name: add github known host key 49 | ansible.builtin.known_hosts: 50 | name: github.com 51 | key: "{{ item }}" 52 | path: /etc/ssh/ssh_known_hosts 53 | state: present 54 | loop: 55 | - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= 56 | - github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== 57 | - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl 58 | - name: remove apparmor 59 | apt: 60 | state: absent 61 | name: 62 | - apparmor 63 | - name: Remove popularity contest 64 | ansible.builtin.file: 65 | path: /etc/cron.daily/popularity-contest 66 | state: absent 67 | - name: install python packages 68 | pip: 69 | extra_args: '--break-system-packages' 70 | name: 71 | - black 72 | - isort 73 | - name: install golang 74 | script: install_golang 75 | environment: 76 | ARCH: "{{ ansible_architecture }}" 77 | VERSION: "1.23.0" 78 | - name: Add sudo/adm group 79 | ansible.builtin.group: 80 | name: "{{ item }}" 81 | state: present 82 | system: true 83 | loop: 84 | - adm 85 | - sudo 86 | - name: Add chrony sources 87 | when: '"greenhouse*" != "${ansible_hostname}"' 88 | copy: 89 | dest: /etc/chrony/sources.d/local.sources 90 | owner: root 91 | mode: 0644 92 | content: | 93 | # Local NTP backups 94 | server 10.10.10.12 iburst 95 | server 10.10.10.10 iburst 96 | pool time.google.com iburst maxsources 3 97 | pool pool.ntp.org iburst maxsources 3 98 | - name: update smw keys 99 | shell: ssh-import-id -o ~smw/.ssh/authorized_keys gh:stephen-mw 100 | run_once: true 101 | - name: setup root user 102 | shell: cp -r /etc/skel/. /root/ 103 | run_once: true 104 | - name: setup sudoers 105 | copy: 106 | dest: /etc/sudoers.d/01smw 107 | content: | 108 | smw ALL=(ALL) NOPASSWD: ALL 109 | # Allow sudo to keep using agent forwarding 110 | Defaults env_keep+=SSH_AUTH_SOCK 111 | - name: custom shell 112 | copy: 113 | src: ../assets/profile.sh 114 | dest: /etc/profile.d/custom.sh 115 | - name: setup vim 116 | copy: 117 | src: ../assets/vimrc 118 | dest: /etc/vim/vimrc.local 119 | - name: vim default 120 | shell: update-alternatives --set editor /usr/bin/vim.basic 121 | - name: setup secret envs file in /etc/profile.d/secrets.sh 122 | copy: 123 | dest: /etc/profile.d/secrets.sh 124 | src: rpirc 125 | - name: setup cron 126 | copy: 127 | dest: /etc/cron.d/ansible 128 | owner: root 129 | mode: 0644 130 | content: | 131 | MAILTO=contact.stephen.wood@gmail.com 132 | BASH_ENV="/etc/profile.d/secrets.sh" 133 | SHELL=/usr/bin/bash 134 | @hourly smw /usr/bin/ssh-import-id gh:stephen-mw 135 | @restart smw /usr/bin/ssh-import-id gh:stephen-mw 136 | - name: lock down sshd 137 | copy: 138 | dest: /etc/ssh/sshd_config.d/00cato.conf 139 | owner: root 140 | mode: 0644 141 | content: | 142 | AllowUsers smw 143 | PasswordAuthentication no 144 | UseDNS no 145 | X11Forwarding yes 146 | X11UseLocalhost no 147 | AddressFamily inet 148 | - name: Configure sysctl settings 149 | copy: 150 | dest: /etc/sysctl.d/00cato.conf 151 | mode: 0644 152 | owner: root 153 | content: | 154 | net.ipv6.conf.all.disable_ipv6 = 1 155 | net.ipv6.conf.default.disable_ipv6 = 1 156 | # Enable dmesg for all users 157 | kernel.dmesg_restrict = 0 158 | -------------------------------------------------------------------------------- /configuration/playbook/install_golang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exuo pipefail 3 | 4 | # These vars must be set by ANSIBLE 5 | # export VERSION="1.20.1" 6 | # export ARCH="armv6l" 7 | 8 | export ROOT=/usr/local/go 9 | 10 | # Support for 32 bit, 64 bit ARM, and 64bit intel 11 | if [[ "${ARCH}" == "armv7l" || "${ARCH}" == "armv6l" ]]; then 12 | ARCH="armv6l" 13 | elif [[ "${ARCH}" == "aarch64" ]]; then 14 | ARCH="arm64" 15 | elif [[ "${ARCH}" == "x86_64" ]]; then 16 | ARCH="amd64" 17 | else 18 | echo "non-compatible architecture version: ${ARCH}" 19 | exit 1 20 | fi 21 | 22 | if go version | grep -q "${VERSION}"; then 23 | echo "Go is already at latest version" 24 | exit 25 | fi 26 | 27 | # Remove any old installed version 28 | if [[ -d "${ROOT}" ]]; then 29 | rm -rfv "${ROOT}" 30 | fi 31 | 32 | cd /tmp 33 | rm -fv "go${VERSION}.linux-${ARCH}.tar.gz" || true 34 | wget "https://go.dev/dl/go${VERSION}.linux-${ARCH}.tar.gz" 35 | sudo tar -C /usr/local -xvf "go${VERSION}.linux-${ARCH}.tar.gz" 36 | sudo ln -sf ${ROOT}/bin/* /usr/bin/ 37 | -------------------------------------------------------------------------------- /configuration/playbook/install_wiringpi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | 5 | export VERSION="2.61-1" 6 | 7 | # Support for 32 bit, 64 bit ARM 8 | if [[ "${ARCH}" == "armv7l" || "${ARCH}" == "armv6l" ]]; then 9 | ARCH="armhf" 10 | elif [[ "${ARCH}" == "aarch64" ]]; then 11 | ARCH="arm64" 12 | else 13 | echo "non-compatible architecture version: ${ARCH}" 14 | exit 1 15 | fi 16 | 17 | if [[ $(dpkg -l wiringpi) ]]; then 18 | echo "Wiring pi already installed" 19 | exit 20 | fi 21 | 22 | cd /tmp 23 | export FILE_URI="https://github.com/WiringPi/WiringPi/releases/download/${VERSION}/wiringpi-${VERSION}-${ARCH}.deb" 24 | 25 | echo "Downloading ${FILE_URI}" 26 | wget "${FILE_URI}" 27 | dpkg -i wiringpi-${VERSION}-${ARCH}.deb 28 | -------------------------------------------------------------------------------- /configuration/playbook/optional/rngtools.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: rpi.local 3 | become: yes 4 | tasks: 5 | - name: install packages 6 | run_once: true 7 | apt: 8 | update_cache: true 9 | state: present 10 | name: 11 | - libcurl3-dev 12 | - libjansson-dev 13 | - libp11-dev 14 | - librtlsdr-dev 15 | - libssl-dev 16 | - libusb-1.0-0-dev 17 | - libxml2 18 | - libxml2-dev 19 | - name: compile and install 20 | run_once: true 21 | shell: | 22 | cd /usr/local/src/ 23 | git clone --depth=50 --branch=master https://github.com/nhorman/rng-tools.git nhorman/rng-tools 24 | cd nhorman/rng-tools 25 | git submodule update --init --recursive 26 | ./autogen 27 | ./configure --without-pkcs11 --without-rtlsdr 28 | make 29 | make install 30 | -------------------------------------------------------------------------------- /configuration/playbook/rpi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: yes 4 | tasks: 5 | - name: setup apt proxy cache 6 | copy: 7 | dest: /etc/apt/apt.conf.d/02mirror 8 | owner: root 9 | mode: 0644 10 | content: '#Acquire::http::Proxy "http://cato.local:3142/";' 11 | - name: set RPI hostname 12 | # This will make it so the dhcp-set hostname will take effect. 13 | run_once: true 14 | copy: 15 | dest: /etc/hostname 16 | content: localhost 17 | # - name: add debian keys 18 | # run_once: true 19 | # shell: sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 648ACFD622F3D138 0E98404D386FA1D9 605C66F00D6C9793 9165938D90FDDD2E 20 | # - name: Update apt sources 21 | # copy: 22 | # dest: /etc/apt/sources.list 23 | # content: | 24 | # # Old mirrors 25 | # deb http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi 26 | # #deb-src http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi 27 | # deb http://archive.raspberrypi.org/debian/ bullseye main 28 | # #deb-src http://archive.raspberrypi.org/debian/ bullseye main 29 | # 30 | # # Local mirrors 31 | # #deb http://mirror.u1f602.com/debian/ bullseye main 32 | # #deb http://mirror.u1f602.com/raspbian/ bullseye main contrib non-free rpi 33 | # #deb-src http://mirror.u1f602.com/debian/ bullseye main 34 | # #deb-src http://mirror.u1f602.com/raspbian/ bullseye main contrib non-free rpi 35 | # - name: Upgrade once to latest version 36 | # run_once: true 37 | # apt: 38 | # upgrade: dist 39 | # - name: Remove old list 40 | # ansible.builtin.file: 41 | # path: /etc/apt/sources.list.d/raspi.list 42 | # state: absent 43 | - name: remove piwiz 44 | apt: 45 | state: absent 46 | name: 47 | - piwiz 48 | - name: install packages 49 | apt: 50 | update_cache: true 51 | state: present 52 | install_recommends: false 53 | name: 54 | - i2c-tools 55 | - libsox-fmt-mp3 56 | - pavucontrol 57 | - pigpio 58 | - rpi-update 59 | - sox 60 | - xinit 61 | - xserver-xorg 62 | #- name: setup watchdog 63 | # blockinfile: 64 | # dest: /etc/watchdog.conf 65 | # content: | 66 | # realtime = yes 67 | # priority = 1 68 | # watchdog-device = /dev/watchdog 69 | # watchdog-timeout = 15 70 | # max-load-1 = 24 71 | # interface = wlan0 72 | # TODO this is not yet working 73 | # https://github.com/RPi-Distro/repo/issues/237 74 | #- name: start watchdog 75 | # ansible.builtin.systemd: 76 | # name: watchdog 77 | # enabled: true 78 | # state: stopped 79 | # This currently breaks the pi's bluetooth 80 | # - name: Upgrade RPI firmware 81 | # run_once: true 82 | # shell: yes | rpi-update || true 83 | - name: setup swood to updated groups 84 | user: 85 | name: smw 86 | comment: Stephen Wood 87 | groups: 88 | - adm 89 | - bluetooth 90 | - gpio 91 | - i2c 92 | #- pulse-access 93 | - sudo 94 | - name: Remove the default pi user 95 | user: 96 | name: pi 97 | state: absent 98 | remove: yes 99 | force: true 100 | - name: setup syslog 101 | copy: 102 | dest: /etc/rsyslog.d/custom.conf 103 | content: '*.* @cato.u1f602.com:514' 104 | - name: disable ipv6 105 | blockinfile: 106 | path: /etc/sysctl.conf 107 | block: | 108 | # Disable ipv6 109 | net.ipv6.conf.all.disable_ipv6 = 1 110 | - name: enable RNG config 111 | blockinfile: 112 | path: /etc/default/rng-tools-debian 113 | block: | 114 | HRNGDEVICE=/dev/hwrng 115 | - name: Setup boot.txt 116 | blockinfile: 117 | dest: /boot/config.txt 118 | marker: "### rpi.yml ansible block ###" 119 | block: | 120 | [all] 121 | dtparam=watchdog=on 122 | - name: load i2c in modules 123 | blockinfile: 124 | dest: /etc/modules-load.d/modules.conf 125 | content: i2c-dev 126 | - name: Enable systemd watchdog 127 | blockinfile: 128 | path: /etc/systemd/system.conf 129 | backup: yes 130 | block: | 131 | RuntimeWatchdogSec=10 132 | ShutdownWatchdogSec=10min 133 | RebootWatchdogSec=10min 134 | WatchdogDevice=/dev/watchdog 135 | - name: disable swap 136 | ansible.builtin.systemd: 137 | masked: true 138 | name: dphys-swapfile 139 | # - name: setup unattended upgrades 140 | # apt: 141 | # state: present 142 | # name: 143 | # - apt-listchanges 144 | # - bsd-mailx 145 | # - unattended-upgrades 146 | # - name: setup /etc/apt/apt.conf.d/50unattended-upgrades 147 | # copy: 148 | # dest: /etc/apt/apt.conf.d/50unattended-upgrades 149 | # content: | 150 | # Unattended-Upgrade::Mail "contact.stephen.wood@gmail.com"; 151 | # Unattended-Upgrade::Automatic-Reboot "true"; 152 | # Unattended-Upgrade::Automatic-Reboot-Time "14:00"; 153 | # - name: setup /etc/apt/apt.conf.d/20auto-upgrades 154 | # copy: 155 | # dest: /etc/apt/apt.conf.d/20auto-upgrades 156 | # content: | 157 | # APT::Periodic::Update-Package-Lists "1"; 158 | # APT::Periodic::Download-Upgradeable-Packages "1"; 159 | # APT::Periodic::Unattended-Upgrade "1"; 160 | # APT::Periodic::Verbose "1"; 161 | # APT::Periodic::AutocleanInterval "7"; 162 | # - name: enable unattended upgrades 163 | # run_once: true 164 | # shell: "dpkg-reconfigure --priority=low unattended-upgrades -fnoninteractive" 165 | - name: remove pi user 166 | user: 167 | name: pi 168 | state: absent 169 | remove: true 170 | - name: Disable auto-login 171 | ansible.builtin.file: 172 | path: /etc/systemd/system/getty@tty1.service.d/autologin.conf 173 | state: absent 174 | - name: install wiring pi 175 | script: install_wiringpi 176 | environment: 177 | ARCH: "{{ ansible_architecture }}" 178 | -------------------------------------------------------------------------------- /configuration/setup_pi: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | # My raspberry pi setup file 5 | # 6 | # Warning! This will download my public keys to your user. Change this before 7 | # running it! 8 | # 9 | # Author: Stephen Wood (www.heystephenwood.com) 10 | 11 | # Our new user -- me 12 | export NEW_USER="smw" 13 | 14 | # My github user. Will pull these public keys 15 | export GITHUB_USER="stephen-mw" 16 | 17 | function setup_ramfs(){ 18 | # Improve the life of the SD by limiting the /logs and /tmp directory to 19 | # a ramfs. In the future I'll probably just ship off logs with rsyslog. 20 | for dir in /var/log /tmp; do 21 | if ! grep -qP "tmpfs\s+${dir}" /etc/fstab; then 22 | echo -e "tmpfs\t${dir}\ttmpfs\tdefaults,noatime,nosuid,mode=0774,size=25m\t0 0\n" >> /etc/fstab 23 | fi 24 | done 25 | 26 | chmod 1777 /tmp 27 | } 28 | 29 | function setup_locale(){ 30 | # Generate locales for US UTF-8 31 | sed -i 's/^en_GB.UTF-8 UTF-8/# en_GB.UTF-8 UTF-8/' /etc/locale.gen 32 | sed -i 's/^# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen 33 | 34 | locale-gen 35 | 36 | # Set language to EN 37 | export LANG=en_US.UTF-8 38 | export LC_ALL=en_US.UTF-8 39 | export LC_MESSAGES=en_US.UTF-8 40 | 41 | cat > /etc/default/locale< /etc/default/keyboard< /etc/apt/sources.list<> /etc/apt/apt.conf.d/50unattended-upgrades< /etc/apt/apt.conf.d/20auto-upgrades< /etc/vim/vimrc.local 146 | } 147 | 148 | function setup_user() { 149 | if ! id -u ${NEW_USER}; then 150 | useradd -m -s /bin/bash -G sudo ${NEW_USER} 151 | fi 152 | 153 | if ! [[ -d ~smw/.ssh ]]; then 154 | mkdir ~smw/.ssh 155 | fi 156 | 157 | ssh-import-id gh:stephen-mw -o ~smw/.ssh/authorized_keys 158 | chmod 600 ~smw/.ssh/authorized_keys 159 | chown -R smw:smw ~smw 160 | 161 | # Banish the Pi user 162 | if id -u pi; then 163 | userdel -rf pi 164 | fi 165 | 166 | # Remove all sudoers and allow new user to sudo without passsword 167 | test -f /etc/sudoers.d/010_pi-nopasswd && rm -fv /etc/sudoers.d/010_pi-nopasswd 168 | cat >/etc/sudoers.d/01${NEW_USER}< ssh_host_ed25519_pub 193 | 194 | # Turn off password authentication and only allow ssh 195 | cat >> /etc/ssh/sshd_config< '/etc/rsyslog.d/custom.conf' 207 | systemctl restart rsyslog 208 | 209 | # Put your custom functions here 210 | cat >/etc/profile.d/custom.sh<> /boot/config.txt 240 | 241 | cat >> /etc/watchdog.conf< /etc/dnsmasq.d/pimasq.conf < /etc/cron.daily/pimasq_updater <<'UPDATER' 57 | #!/bin/bash 58 | 59 | HOSTFILE="/etc/advert.hosts" 60 | 61 | # Empty the tmp file we'll use to download the files 62 | > $HOSTFILE.raw 63 | 64 | # These are the original (sometimes slow) sources for our hosts. Uncomment these lines 65 | # to draw from them instead of the aggregate mirror. 66 | # 67 | #HOSTS="http://winhelp2002.mvps.org/hosts.txt \ 68 | # http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts \ 69 | # http://hostsfile.mine.nu/Hosts" 70 | # Clear out our host file and make it new again 71 | #for host in $HOSTS; do 72 | # curl -s -L "$host" | grep -P "^127\.0\.0\.1\s" | >> $HOSTFILE.raw 73 | #done 74 | 75 | # The aggregate mirror of the above urls. Updated nightly. 76 | curl -s -L "http://publius.heystephenwood.com/advert.hosts" > $HOSTFILE.raw 77 | 78 | # Moving these files at the end prevents disruption while you're downloading 79 | # the host list. 80 | mv $HOSTFILE.raw $HOSTFILE 81 | 82 | service dnsmasq restart 83 | 84 | UPDATER 85 | 86 | # Things in cron need to be executable 87 | chmod +x /etc/cron.daily/pimasq_updater 88 | 89 | # Run it once so that we have a working hostfile 90 | /etc/cron.daily/pimasq_updater #> /dev/null 91 | 92 | service dnsmasq restart 93 | 94 | cat < /etc/dnsmasq.conf < /etc/network/interfaces.d/ifcfg-eth0 < /etc/dnsmasq.conf < /etc/sysctl.d/99-eth0_bridge.conf 41 | sysctl -p 42 | 43 | # You'll need to switch back to legacy iptables 44 | update-alternatives --set iptables /usr/sbin/iptables-legacy 45 | 46 | echo "If you get errors here about a missing module, you need to reboot and run again." 47 | iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE 48 | 49 | # This will save the rules and restore them on a reboot 50 | -------------------------------------------------------------------------------- /roles/kiwix_server/install_kiwix: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | cd /tmp 5 | wget https://download.kiwix.org/release/kiwix-tools/kiwix-tools_linux-aarch64.tar.gz 6 | tar xvf kiwix-tools_linux-aarch64.tar.gz -C ${BIN_DIR}/ 7 | -------------------------------------------------------------------------------- /roles/kiwix_server/kiwix_server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: yes 4 | tasks: 5 | - name: Add kiwix user 6 | ansible.builtin.user: 7 | name: kiwix 8 | shell: /bin/false 9 | system: yes 10 | home: /etc/kiwix 11 | create_home: true 12 | - name: download binary 13 | script: install_kiwix 14 | environment: 15 | BIN_DIR: "/usr/local/bin" 16 | - name: setup systemd service 17 | copy: 18 | dest: /etc/systemd/system/kiwix.service 19 | content: | 20 | [Unit] 21 | Description=Kiwixi Server 22 | 23 | [Service] 24 | User=kiwix 25 | ExecStart=bash -c '/usr/local/bin/kiwix-serve -p 8080 /etc/kiwix/library/*' 26 | Restart=on-failure 27 | 28 | [Install] 29 | WantedBy=multi-user.target 30 | -------------------------------------------------------------------------------- /roles/octoprint/.gitignore: -------------------------------------------------------------------------------- 1 | config.yml 2 | octoprint-backup-*.zip 3 | -------------------------------------------------------------------------------- /roles/octoprint/Makefile: -------------------------------------------------------------------------------- 1 | default: deploy 2 | 3 | deploy: 4 | ansible-playbook -i "octoprint.local," provision.yml 5 | -------------------------------------------------------------------------------- /roles/octoprint/README.md: -------------------------------------------------------------------------------- 1 | # Updating firmware via plugin 2 | * Install FirmwareUpdater Plugin https://github.com/OctoPrint/OctoPrint-FirmwareUpdater 3 | * Install avrdude on the RaspberryPi, which is used for flashing 4 | * Set FirmwareUpdater Plugin Settings 5 | - Settings > Plugins > open FirmwareUpdater Plugin 6 | - Click wrench on top right 7 | - Flash method: avrdude (Atmel AVR Family) 8 | - AVR MCU: ATmega2560 9 | - Path to avrdude: /usr/bin/avrdude (we installed it in step 2) 10 | - AVR Programmer Type: wiring 11 | * Update firmware 12 | * Check firmware on your printer: Menu > support 13 | * Download latest Firmware: https://www.prusa3d.com/drivers/ 14 | * extract .hex file from downloaded zip 15 | * Open FirmwareUpdater Plugin 16 | * ...from file > Browse > and select .hexfile 17 | * click Flash from file - and wait ... 18 | * Done - check firmware again on your printer under Menu > support 19 | 20 | ## Setting up the webcam 21 | The ansible script will install the spyglass software and start the webcam systemd process. 22 | 23 | You need to go into the settings and add the following streams changing the URL to your raspberry pi: 24 | 25 | - http://octoprint.local:8080/stream 26 | - http://octoprint.local:8080/snapshot 27 | -------------------------------------------------------------------------------- /roles/octoprint/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | vars: 4 | ROOT: "/etc/octoprint" 5 | VENV: "{{ ROOT }}/venv" 6 | become: yes 7 | tasks: 8 | - name: install packages 9 | apt: 10 | update_cache: true 11 | state: present 12 | name: 13 | - avrdude # used for flashing 14 | - libcamera-apps-lite 15 | - prusa-slicer # Only needed for updating firmware 16 | - python3-opengl 17 | - python3-picamera2 18 | - python3-pip 19 | - python3-pyqt5 20 | - python3-virtualenv 21 | - name: Set hostname 22 | ansible.builtin.hostname: 23 | name: octoprint 24 | use: systemd 25 | - name: Update /etc/hosts file 26 | blockinfile: 27 | path: /etc/hosts 28 | block: | 29 | 127.0.0.1 octoprint octoprint.local 30 | - name: add octoprint user 31 | ansible.builtin.user: 32 | name: octoprint 33 | shell: /usr/sbin/nologin 34 | groups: 35 | - dialout 36 | - tty 37 | - video 38 | system: true 39 | create_home: true 40 | home: "{{ ROOT }}" 41 | # Octoprint makes a lot of assumptions about where it's running and doesn't 42 | # like running out of a home directory. We're going to force it to do that 43 | # anyway. 44 | - name: setup virtualenv 45 | script: setup_virtualenv 46 | environment: 47 | ROOT: "{{ ROOT }}" 48 | - name: Install octoprint pip 49 | ansible.builtin.pip: 50 | name: octoprint 51 | state: latest 52 | extra_args: "--break-system-packages" 53 | virtualenv: "{{ VENV }}" 54 | notify: 55 | - restart octoprint service 56 | # This may not be necessary if restoring from a backup 57 | - name: create octoprint settings 58 | ansible.builtin.copy: 59 | src: config.yml 60 | dest: "{{ ROOT }}/config.yml" 61 | owner: octoprint 62 | mode: '0644' 63 | directory_mode: 755 64 | - name: setup spyglass 65 | script: setup_spyglass 66 | environment: 67 | DESTINATION: "/usr/local/src/spyglass" 68 | # Start the webcam service 69 | - name: Copy webcam systemd service 70 | ansible.builtin.template: 71 | src: webcam.systemd 72 | dest: /etc/systemd/system/webcam.service 73 | mode: '0440' 74 | - name: Start webcam service 75 | ansible.builtin.systemd: 76 | daemon_reload: yes 77 | state: started 78 | name: webcam 79 | enabled: yes 80 | - name: set directory permissions 81 | file: 82 | path: "{{ ROOT }}" 83 | owner: octoprint 84 | recurse: True 85 | group: octoprint 86 | # Start the service 87 | - name: Download octoprint systemd unit 88 | ansible.builtin.template: 89 | src: service.systemd 90 | dest: /etc/systemd/system/octoprint.service 91 | mode: '0440' 92 | - name: start octoprint service 93 | ansible.builtin.systemd: 94 | daemon_reload: yes 95 | state: started 96 | name: octoprint 97 | enabled: true 98 | handlers: 99 | - name: restart octoprint service 100 | ansible.builtin.systemd: 101 | daemon_reload: yes 102 | state: restarted 103 | name: octoprint 104 | enabled: yes 105 | 106 | -------------------------------------------------------------------------------- /roles/octoprint/service.systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=The snappy web interface for your 3D printer 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | # ROOT is configured via ansible 8 | Environment="LC_ALL=C.UTF-8" 9 | Environment="LANG=C.UTF-8" 10 | Type=exec 11 | User=octoprint 12 | ExecStart={{ VENV}}/bin/octoprint serve --port=80 --config {{ ROOT }}/config.yml --basedir {{ ROOT }} 13 | AmbientCapabilities=CAP_NET_BIND_SERVICE 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/octoprint/setup_spyglass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | 5 | # DESTINATION set by ansible 6 | if [[ ! -d "${DESTINATION}" ]]; then 7 | git clone https://github.com/roamingthings/spyglass.git ${DESTINATION} 8 | fi 9 | 10 | cd ${DESTINATION} 11 | git pull 12 | pip install -r requirements.txt --break-system-packages 13 | pip install -e . --break-system-packages 14 | -------------------------------------------------------------------------------- /roles/octoprint/setup_virtualenv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if [[ -d ${ROOT}/venv ]]; then 5 | echo "Octoprint virtualenv already exists!" 6 | exit 0 7 | fi 8 | 9 | cd ${ROOT} 10 | python -m virtualenv venv 11 | -------------------------------------------------------------------------------- /roles/octoprint/webcam.systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start RPI webcam for octoprint 3 | 4 | [Service] 5 | User=octoprint 6 | ExecStart=/usr/local/src/spyglass/run.py -ud -fh --fps=30 -r 1920x1080 7 | RestartSec=3 8 | Restart=always 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /roles/ospi/ospi.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=securitypi 3 | 4 | [Service] 5 | AmbientCapabilities=CAP_NET_BIND_SERVICE 6 | User=root 7 | WorkingDirectory={{ ROOT }} 8 | ExecStart={{ ROOT }}/OpenSprinkler 9 | Restart=on-failure 10 | RestartSec=5 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /roles/ospi/provision.yml: -------------------------------------------------------------------------------- 1 | # Ansible config script for opensprinkler pi 2 | # https://opensprinkler.com/ 3 | --- 4 | - hosts: all 5 | vars: 6 | ROOT: "/etc/ospi" 7 | become: yes 8 | tasks: 9 | - name: install packages 10 | apt: 11 | update_cache: true 12 | state: present 13 | name: 14 | - build-essential 15 | - git 16 | - name: Set hostname 17 | ansible.builtin.hostname: 18 | name: sprinkler 19 | use: systemd 20 | - name: Update /etc/hosts file 21 | blockinfile: 22 | path: /etc/hosts 23 | block: | 24 | 127.0.0.1 sprinkler sprinkler.local 25 | - name: add ospi user 26 | ansible.builtin.user: 27 | name: ospi 28 | shell: /usr/sbin/nologin 29 | groups: 30 | - gpio 31 | system: true 32 | create_home: true 33 | home: "{{ ROOT }}" 34 | - name: checkout OSPI software 35 | ansible.builtin.git: 36 | repo: 'https://github.com/OpenSprinkler/OpenSprinkler-Firmware.git' 37 | dest: "{{ ROOT }}" 38 | version: master 39 | force: true 40 | - name: build OSPI software 41 | shell: | 42 | set -euo pipefail 43 | cd {{ ROOT }} 44 | ./build.sh ospi 45 | args: 46 | executable: /bin/bash 47 | - name: set OSPI directory permissions 48 | file: 49 | path: "{{ ROOT }}" 50 | owner: root 51 | group: ospi 52 | mode: u=rwX,g=rwX,o= 53 | recurse: true 54 | # Drop permission on the actual binary. This will overwrite what happened 55 | # in the previous changes but necessary for security (the binary should 56 | # never be able to update itself). 57 | - name: set OSPI binary permission 58 | file: 59 | path: "{{ ROOT }}/OpenSprinkler" 60 | owner: ospi 61 | mode: "0750" 62 | # Copy the systemd file and start the service 63 | - name: Create ospi systemd file 64 | ansible.builtin.template: 65 | src: ospi.service 66 | dest: /etc/systemd/system/ospi.service 67 | mode: '0444' 68 | - name: start ospi service 69 | ansible.builtin.systemd: 70 | daemon_reload: yes 71 | state: started 72 | name: ospi 73 | enabled: true 74 | -------------------------------------------------------------------------------- /roles/scanny/Makefile: -------------------------------------------------------------------------------- 1 | default: deploy 2 | 3 | deploy: 4 | ansible-playbook -i "scanny.local," provision.yml 5 | -------------------------------------------------------------------------------- /roles/scanny/install_drivers: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if [[ -d "/usr/local/src/linux-UFRII-drv-v570-us" ]]; then 5 | echo "Drivers already present. Refusing to install." 6 | exit 7 | fi 8 | 9 | # Sanity check that env variable set by ansible 10 | echo "Using drivers located at ${DRIVERS}" 11 | 12 | cd /usr/local/src 13 | tar xvf linux-UFRII-drv-v570-us-18.tar.gz 14 | cd linux-UFRII-drv-v570-us 15 | yes | sudo bash ./install.sh 16 | -------------------------------------------------------------------------------- /roles/scanny/print_server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | apt update -y 5 | 6 | # The foo2zjs are for Dell laser prints, the gutenberg drivers are for other printers. 7 | apt install -y \ 8 | cups \ 9 | libsane-extras \ 10 | printer-driver-gutenprint \ 11 | sane \ 12 | sane-utils 13 | 14 | # This is the user I run my scanning software on. The pass w enerated with 15 | # something like this: 16 | # openssl passwd -6 -salt xyz yourpass 17 | mkdir -p /etc/scanny 18 | useradd -d /etc/scanny -p '$6$97a6f$MIw8LxJpwTsbAtQ4QcnoPVYPyO/X/j2ht6EnSAXDsGTC1OKb2Wl8Faz570q9WBuRWf2SDlNkeufmkKv/031CO0' -G lp,lpadmin,scanner -s /usr/sbin/nologin --system scanny 19 | 20 | # Anyone can print! 21 | sudo cupsctl --remote-any 22 | 23 | # Make sure scanny user can modify/admin CUPS 24 | perl -i.original -pe 's|^ Require user @SYSTEM| Require user @SYSTEM scanny |g' /etc/cups/cupsd.conf 25 | -------------------------------------------------------------------------------- /roles/scanny/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: scanner playbook 3 | hosts: all 4 | become: yes 5 | tasks: 6 | - name: Set hostname 7 | ansible.builtin.hostname: 8 | name: 'scanny' 9 | - name: install packages 10 | apt: 11 | update_cache: true 12 | state: present 13 | name: 14 | - cups 15 | - libsane1 16 | - libcups2 17 | - printer-driver-gutenprint 18 | - sane 19 | - sane-utils 20 | - name: setup canon printer drivers 21 | ansible.builtin.copy: 22 | src: linux-UFRII-drv-v570-us-18.tar.gz 23 | dest: /usr/local/src/ 24 | # This will exit code failure the first time 25 | - name: install drivers 26 | script: install_drivers 27 | environment: 28 | DRIVERS: "linux-UFRII-drv-v570-us-18" 29 | - name: create user 30 | ansible.builtin.user: 31 | name: scanny 32 | shell: /usr/sbin/nologin 33 | system: yes 34 | group: 35 | - lp 36 | - lpadmin 37 | password: '$6$97a6f$MIw8LxJpwTsbAtQ4QcnoPVYPyO/X/j2ht6EnSAXDsGTC1OKb2Wl8Faz570q9WBuRWf2SDlNkeufmkKv/031CO0' 38 | createhome: yes 39 | home: /etc/scanny 40 | - name: Allow anyone to print 41 | command: cupsctl --remote-any 42 | - name: Make sure scanny user can modify/admin CUPS 43 | replace: 44 | path: /etc/cups/cupsd.conf 45 | regexp: '^ Require user @SYSTEM' 46 | replace: ' Require user @SYSTEM scanny' 47 | 48 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/README.md: -------------------------------------------------------------------------------- 1 | # Startum 1 NTP server on a Raspberry pi 2 | This is the ansible script you can use to provision a raspberry pi as a high-accuracy NTP server. 3 | 4 | Your raspberry pi will automatically set time based on GPS satellites and can keep very accurate time with the PPS output from the receiver. 5 | 6 | These scripts will also install chrony so that the time can be served on your home network or as part of the ntppool.org project. 7 | 8 | Requirements: 9 | 1. Raspberry pi model 3 or above 10 | 2. Serial GPS device such as a cheap ublox 6m with pps output 11 | 3. A ds1307 real-time hardware clock 12 | 13 | Wire the PPS output from the gps to pin 18 on the raspberry pi and connect the ds1307 to the i2c bus. The GPS device should be connected to the serial pins. 14 | 15 | You can run this ansible script directly like so: 16 | ``` 17 | ansible-playbook -i "," provision.yml 18 | ``` 19 | 20 | ## Useful commands 21 | Identify the GPs hardware 22 | ``` 23 | $ gpsctl /dev/ttyAMA0 24 | 25 | /dev/ttyAMA0 identified as a u-blox SW 7.03 (45969),HW 00040007 at 9600 baud. 26 | ``` 27 | Put the receiver into NMEA sentence mode 28 | ``` 29 | gpsctl -n /dev/ttyAMA0 30 | ``` 31 | 32 | Additional configuration options can be explored via [ubxtool](https://gpsd.io/ubxtool-examples.html) but the default settings are sufficient for an NTP server. 33 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/chrony.conf: -------------------------------------------------------------------------------- 1 | # Include configuration files found in /etc/chrony/conf.d. 2 | confdir /etc/chrony/conf.d 3 | 4 | # Use time sources from DHCP. 5 | sourcedir /run/chrony-dhcp 6 | 7 | # These servers act essentially as a backup -- in case the pps refclock is 8 | # unavailable. We use google because they support leap-second smearing. 9 | # 10 | # maxpoll 5 keeps the request window within the ARP table timeout 11 | server time1.google.com iburst maxpoll 5 12 | server time2.google.com iburst maxpoll 5 13 | server time3.google.com iburst maxpoll 5 14 | server time4.google.com iburst maxpoll 5 15 | 16 | # This will only get time from the GPS module 17 | refclock PPS /dev/pps0 lock NMEA refid GPS trust prefer 18 | refclock SHM 0 offset 0.5 refid NMEA noselect 19 | 20 | # Save NTS keys and cookies. 21 | ntsdumpdir /var/lib/chrony 22 | 23 | cmdallow 127.0.0.1 24 | 25 | # Serve time to anyone 26 | allow 27 | 28 | # Broadcast this NTP server on the local network every 60s 29 | broadcast 60 10.10.10.255 30 | 31 | # Rate limite clients from being too bursty 32 | ratelimit interval 1 burst 16 33 | 34 | # This is a stratum-1 timeserver after all 35 | local stratum 1 36 | 37 | # This directive specify the file into which chronyd will store the rate 38 | # information. 39 | driftfile /var/lib/chrony/chrony.drift 40 | 41 | # Allow the system clock to be stepped in the first 300 updates if its offset 42 | # is greater than 1s. 43 | makestep 1 300 44 | 45 | # Update the rtc device periodically 46 | rtcsync 47 | 48 | # Because the GPS timesource is set to "required" we can leave this at 1. It 49 | # will update the system clock only after the GPS clock comes online. 50 | minsources 1 51 | 52 | # Specify directory for log files. 53 | logdir /var/log/chrony 54 | 55 | # Select which information is logged. 56 | log measurements statistics tracking 57 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/chrony.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Conflicts=systemd-timesyncd.service openntpd.service 3 | Description=Chrony Time Daemon 4 | After=gpsd.service 5 | 6 | [Service] 7 | User=root 8 | ExecStart=/usr/sbin/chronyd -d -m -r 9 | KillMode=process 10 | Restart=on-failure 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/compile_gpsd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Don't reinstall if it already exists 5 | export VERSION=3.25 6 | if gpsd --version | grep -q ${VERSION}; then 7 | echo "gpsd already installed at ${VERSION}" 8 | exit 9 | fi 10 | 11 | # Most of the python libraries below are for using xgps, and can be skipped 12 | # if you don't plan to run xgps 13 | sudo apt remove -y --purge gpsd 14 | sudo apt install -y \ 15 | build-essential \ 16 | libdbus-1-dev \ 17 | libgtk-3-dev \ 18 | libncurses5-dev \ 19 | manpages-dev \ 20 | pkg-config \ 21 | pps-tools \ 22 | python3-dev \ 23 | python-gi-dev \ 24 | python3-cairo \ 25 | python3-gi-cairo \ 26 | python3-matplotlib \ 27 | python3-serial \ 28 | scons 29 | 30 | # For xgps 31 | pip install -U gps pyserial --break-system-packages 32 | 33 | export URI="http://download.savannah.gnu.org/releases/gpsd/gpsd-${VERSION}.zip" 34 | 35 | cd /tmp 36 | rm -rfv gpsd-${VERSION} || true 37 | wget ${URI} 38 | unp gpsd-${VERSION}.zip 39 | cd gpsd-${VERSION} 40 | 41 | # Install the software 42 | scons -c 43 | scons 44 | scons check 45 | scons install 46 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/gpsd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=GPS daemon for Raspberry Pi 3 | BindsTo=dev-ttyAMA0.device 4 | After=dev-ttyAMA0.device 5 | 6 | [Service] 7 | ExecStart=/usr/local/sbin/gpsd -F /var/run/gpsd.sock -b -N -n /dev/ttyAMA0 /dev/pps0 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/i2c_check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Used to alert when i2c settings are wrong or cords get jiggled in the RPI 5 | 6 | EXPECTED=$(cat <<'HEREDOC' 7 | 0 1 2 3 4 5 6 7 8 9 a b c d e f 8 | 00: -- -- -- -- -- -- -- -- 9 | 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 10 | 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 11 | 30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- -- 12 | 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 13 | 50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 14 | 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 15 | 70: -- -- -- -- -- -- -- -- 16 | HEREDOC 17 | ) 18 | 19 | EXPECTED=$(echo "$EXPECTED" | xargs) 20 | CURRENT=$(sudo i2cdetect -y 1) 21 | CURRENT=$(echo "$CURRENT" | xargs) 22 | 23 | if [[ "$CURRENT" != "$EXPECTED" ]]; then 24 | echo "The current I2C bus does not match the expected output." 25 | echo "Expected:" 26 | echo "$EXPECTED" 27 | echo "Current:" 28 | echo "$CURRENT" 29 | exit 1 30 | fi 31 | -------------------------------------------------------------------------------- /roles/stratum_1_timeserver/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: yes 4 | tasks: 5 | - name: install packages 6 | apt: 7 | update_cache: true 8 | state: present 9 | name: 10 | - chrony 11 | - pps-tools 12 | - unp 13 | - name: compile gpsd 14 | script: compile_gpsd 15 | - name: set PYTHONPATH for xgps 16 | blockinfile: 17 | dest: /etc/bash.bashrc 18 | content: "export PYTHONPATH=${PYTHONPATH}:/usr/local/lib/python3/dist-packages" 19 | - name: setup chrony conf 20 | copy: 21 | src: ./chrony.conf 22 | dest: /etc/chrony/chrony.conf 23 | - name: setup chrony service 24 | copy: 25 | src: ./chrony.service 26 | dest: /etc/systemd/system/chrony.service 27 | - name: setup gpsd systemd service 28 | copy: 29 | src: ./gpsd.service 30 | dest: /etc/systemd/system/gpsd.service 31 | mode: 0644 32 | - name: Mask serial-getty@ttyAMA0 33 | ansible.builtin.systemd: 34 | name: serial-getty@ttyAMA0 35 | masked: true 36 | - name: Mask gpsd.socket 37 | ansible.builtin.systemd: 38 | name: gpsd.socket 39 | masked: true 40 | - name: enable modules for ntp 41 | copy: 42 | dest: /etc/modules-load.d/ntp.server 43 | content: | 44 | pps-gpio 45 | i2c-dev 46 | rtc-ds1307 47 | # setup i2c, serial, and rtc 48 | - name: enable modules for ntp 49 | blockinfile: 50 | dest: /boot/config.txt 51 | marker: "#### {mark} NTP #####" 52 | marker_begin: "START" 53 | marker_end: "END" 54 | content: | 55 | [all] 56 | # Enable the system watchdog 57 | dtparam=watchdog=on 58 | # Set the pps pin 59 | dtoverlay=pps-gpio,gpiopin=18 60 | # Enable i2c1 and hardware clock 61 | dtparam=i2c1=on 62 | dtoverlay=i2c-rtc,ds1307 63 | # Enable serial and disable bluetooth since it interferes with serial 64 | enable_uart=1 65 | # For raspberry pi 4+ 66 | #dtoverlay=disable-bt 67 | # On RPI3 68 | dtoverlay=pi3-disable-bt 69 | # Disable the old fake hwclock stuff 70 | - name: Remove fakehwclock package 71 | ansible.builtin.apt: 72 | name: fake-hwclock 73 | state: absent 74 | - name: Disable old hwclock-set udev rule 75 | ansible.builtin.file: 76 | dest: /lib/udev/hwclock-set 77 | state: absent 78 | - name: stop fake hwclock 79 | ansible.builtin.systemd: 80 | name: fake-hwclock 81 | masked: true 82 | - name: Update hwclock every hour 83 | copy: 84 | dest: /etc/cron.hourly/hwclock 85 | content: | 86 | #!/bin/bash 87 | # Update the real hwclock every hour 88 | set -euo pipefail 89 | /usr/sbin/hwclock --systohc 90 | mode: 0744 91 | # Stop serial from trying to restart pi 92 | - name: stop serial restart 93 | blockinfile: 94 | dest: /etc/sysctl.conf 95 | marker: "#### NTP #####" 96 | content: kernel.sysrq=0 97 | - name: Stop console from using serial 98 | replace: 99 | path: /boot/cmdline.txt 100 | regexp: 'console=serial0,115200 console=tty1 ' 101 | replace: '' 102 | # i2c check file 103 | - name: setup chrony service 104 | copy: 105 | src: i2c_check 106 | dest: /usr/bin/i2c_check 107 | mode: 0755 108 | owner: root 109 | group: root 110 | - name: start gpsd 111 | ansible.builtin.systemd: 112 | name: "{{ item }}" 113 | enabled: true 114 | state: started 115 | with_items: 116 | - gpsd 117 | - chrony 118 | --------------------------------------------------------------------------------