From c65b4d3c445accaa4f7b8af41aaf89cd17ac1322 Mon Sep 17 00:00:00 2001 From: Pierrick C Date: Sun, 12 Aug 2018 13:17:59 +0200 Subject: [PATCH] Move from waveshare e-ink display to pimoroni's inkyphat (run install_inkyphat_lib.sh to install dependencies) --- raspberry/python/epd2in13.py | 271 ------ raspberry/python/epdif.py | 63 -- raspberry/python/install_inkyphat_lib.sh | 1118 ++++++++++++++++++++++ raspberry/python/mqtt2epaper.py | 140 ++- 4 files changed, 1185 insertions(+), 407 deletions(-) delete mode 100644 raspberry/python/epd2in13.py delete mode 100644 raspberry/python/epdif.py create mode 100644 raspberry/python/install_inkyphat_lib.sh diff --git a/raspberry/python/epd2in13.py b/raspberry/python/epd2in13.py deleted file mode 100644 index c437b31..0000000 --- a/raspberry/python/epd2in13.py +++ /dev/null @@ -1,271 +0,0 @@ -## - # @filename : epd2in13.py - # @brief : Implements for e-paper library - # @author : Yehui from Waveshare - # - # Copyright (C) Waveshare September 9 2017 - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documnetation files (the "Software"), to deal - # in the Software without restriction, including without limitation the rights - # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - # copies of the Software, and to permit persons to whom the Software is - # furished to do so, subject to the following conditions: - # - # The above copyright notice and this permission notice shall be included in - # all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - # THE SOFTWARE. - # - -import epdif -from PIL import Image -import RPi.GPIO as GPIO - -# Display resolution -EPD_WIDTH = 128 -EPD_HEIGHT = 250 - -# EPD2IN13 commands -DRIVER_OUTPUT_CONTROL = 0x01 -BOOSTER_SOFT_START_CONTROL = 0x0C -GATE_SCAN_START_POSITION = 0x0F -DEEP_SLEEP_MODE = 0x10 -DATA_ENTRY_MODE_SETTING = 0x11 -SW_RESET = 0x12 -TEMPERATURE_SENSOR_CONTROL = 0x1A -MASTER_ACTIVATION = 0x20 -DISPLAY_UPDATE_CONTROL_1 = 0x21 -DISPLAY_UPDATE_CONTROL_2 = 0x22 -WRITE_RAM = 0x24 -WRITE_VCOM_REGISTER = 0x2C -WRITE_LUT_REGISTER = 0x32 -SET_DUMMY_LINE_PERIOD = 0x3A -SET_GATE_TIME = 0x3B -BORDER_WAVEFORM_CONTROL = 0x3C -SET_RAM_X_ADDRESS_START_END_POSITION = 0x44 -SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45 -SET_RAM_X_ADDRESS_COUNTER = 0x4E -SET_RAM_Y_ADDRESS_COUNTER = 0x4F -TERMINATE_FRAME_READ_WRITE = 0xFF - -class EPD: - def __init__(self): - self.reset_pin = epdif.RST_PIN - self.dc_pin = epdif.DC_PIN - self.busy_pin = epdif.BUSY_PIN - self.width = EPD_WIDTH - self.height = EPD_HEIGHT - self.lut = self.lut_full_update - - lut_full_update = [ - 0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - - lut_partial_update = [ - 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - - def digital_write(self, pin, value): - epdif.epd_digital_write(pin, value) - - def digital_read(self, pin): - return epdif.epd_digital_read(pin) - - def delay_ms(self, delaytime): - epdif.epd_delay_ms(delaytime) - - def send_command(self, command): - self.digital_write(self.dc_pin, GPIO.LOW) - # the parameter type is list but not int - # so use [command] instead of command - epdif.spi_transfer([command]) - - def send_data(self, data): - self.digital_write(self.dc_pin, GPIO.HIGH) - # the parameter type is list but not int - # so use [data] instead of data - epdif.spi_transfer([data]) - - def init(self, lut): - if (epdif.epd_init() != 0): - return -1 - # EPD hardware init start - self.lut = lut - self.reset() - self.send_command(DRIVER_OUTPUT_CONTROL) - self.send_data((EPD_HEIGHT - 1) & 0xFF) - self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) - self.send_data(0x00) # GD = 0 SM = 0 TB = 0 - self.send_command(BOOSTER_SOFT_START_CONTROL) - self.send_data(0xD7) - self.send_data(0xD6) - self.send_data(0x9D) - self.send_command(WRITE_VCOM_REGISTER) - self.send_data(0xA8) # VCOM 7C - self.send_command(SET_DUMMY_LINE_PERIOD) - self.send_data(0x1A) # 4 dummy lines per gate - self.send_command(SET_GATE_TIME) - self.send_data(0x08) # 2us per line - self.send_command(DATA_ENTRY_MODE_SETTING) - self.send_data(0x03) # X increment Y increment - self.set_lut(self.lut) - # EPD hardware init end - return 0 - - def wait_until_idle(self): - while(self.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy - self.delay_ms(20) -## - # @brief: module reset. - # often used to awaken the module in deep sleep, - ## - def reset(self): - self.digital_write(self.reset_pin, GPIO.LOW) # module reset - self.delay_ms(200) - self.digital_write(self.reset_pin, GPIO.HIGH) - self.delay_ms(200) - -## - # @brief: set the look-up table register - ## - def set_lut(self, lut): - self.lut = lut - self.send_command(WRITE_LUT_REGISTER) - # the length of look-up table is 30 bytes - for i in range(0, len(lut)): - self.send_data(self.lut[i]) - -## - # @brief: convert an image to a buffer - ## - def get_frame_buffer(self, image): - buf = [0x00] * (self.width * self.height / 8) - # Set buffer to value of Python Imaging Library image. - # Image must be in mode 1. - image_monocolor = image.convert('1') - imwidth, imheight = image_monocolor.size - if imwidth != self.width or imheight != self.height: - raise ValueError('Image must be same dimensions as display \ - ({0}x{1}).' .format(self.width, self.height)) - - pixels = image_monocolor.load() - for y in range(self.height): - for x in range(self.width): - # Set the bits for the column of pixels at the current position. - if pixels[x, y] != 0: - buf[(x + y * self.width) / 8] |= 0x80 >> (x % 8) - return buf - -## - # @brief: put an image to the frame memory. - # this won't update the display. - ## - def set_frame_memory(self, image, x, y): - if (image == None or x < 0 or y < 0): - return - image_monocolor = image.convert('1') - image_width, image_height = image_monocolor.size - # x point must be the multiple of 8 or the last 3 bits will be ignored - x = x & 0xF8 - image_width = image_width & 0xF8 - if (x + image_width >= self.width): - x_end = self.width - 1 - else: - x_end = x + image_width - 1 - if (y + image_height >= self.height): - y_end = self.height - 1 - else: - y_end = y + image_height - 1 - self.set_memory_area(x, y, x_end, y_end) - # send the image data - pixels = image_monocolor.load() - byte_to_send = 0x00 - for j in range(y, y_end + 1): - self.set_memory_pointer(x, j) - self.send_command(WRITE_RAM) - # 1 byte = 8 pixels, steps of i = 8 - for i in range(x, x_end + 1): - # Set the bits for the column of pixels at the current position. - if pixels[i - x, j - y] != 0: - byte_to_send |= 0x80 >> (i % 8) - if (i % 8 == 7): - self.send_data(byte_to_send) - byte_to_send = 0x00 - -## - # @brief: clear the frame memory with the specified color. - # this won't update the display. - ## - def clear_frame_memory(self, color): - self.set_memory_area(0, 0, self.width - 1, self.height - 1) - self.set_memory_pointer(0, 0) - self.send_command(WRITE_RAM) - # send the color data - for i in range(0, int(self.width / 8 * self.height)): - self.send_data(color) - -## - # @brief: update the display - # there are 2 memory areas embedded in the e-paper display - # but once this function is called, - # the the next action of SetFrameMemory or ClearFrame will - # set the other memory area. - ## - def display_frame(self): - self.send_command(DISPLAY_UPDATE_CONTROL_2) - self.send_data(0xC4) - self.send_command(MASTER_ACTIVATION) - self.send_command(TERMINATE_FRAME_READ_WRITE) - self.wait_until_idle() - -## - # @brief: specify the memory area for data R/W - ## - def set_memory_area(self, x_start, y_start, x_end, y_end): - self.send_command(SET_RAM_X_ADDRESS_START_END_POSITION) - # x point must be the multiple of 8 or the last 3 bits will be ignored - self.send_data((x_start >> 3) & 0xFF) - self.send_data((x_end >> 3) & 0xFF) - self.send_command(SET_RAM_Y_ADDRESS_START_END_POSITION) - self.send_data(y_start & 0xFF) - self.send_data((y_start >> 8) & 0xFF) - self.send_data(y_end & 0xFF) - self.send_data((y_end >> 8) & 0xFF) - -## - # @brief: specify the start point for data R/W - ## - def set_memory_pointer(self, x, y): - self.send_command(SET_RAM_X_ADDRESS_COUNTER) - # x point must be the multiple of 8 or the last 3 bits will be ignored - self.send_data((x >> 3) & 0xFF) - self.send_command(SET_RAM_Y_ADDRESS_COUNTER) - self.send_data(y & 0xFF) - self.send_data((y >> 8) & 0xFF) - self.wait_until_idle() - -## - # @brief: After this command is transmitted, the chip would enter the - # deep-sleep mode to save power. - # The deep sleep mode would return to standby by hardware reset. - # You can use reset() to awaken or init() to initialize - ## - def sleep(self): - self.send_command(DEEP_SLEEP_MODE) - self.wait_until_idle() - -### END OF FILE ### - diff --git a/raspberry/python/epdif.py b/raspberry/python/epdif.py deleted file mode 100644 index 3378794..0000000 --- a/raspberry/python/epdif.py +++ /dev/null @@ -1,63 +0,0 @@ -## - # @filename : epdif.py - # @brief : EPD hardware interface implements (GPIO, SPI) - # @author : Yehui from Waveshare - # - # Copyright (C) Waveshare July 10 2017 - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documnetation files (the "Software"), to deal - # in the Software without restriction, including without limitation the rights - # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - # copies of the Software, and to permit persons to whom the Software is - # furished to do so, subject to the following conditions: - # - # The above copyright notice and this permission notice shall be included in - # all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - # THE SOFTWARE. - # - -import spidev -import RPi.GPIO as GPIO -import time - -# Pin definition -RST_PIN = 17 -DC_PIN = 25 -CS_PIN = 8 -BUSY_PIN = 24 - -# SPI device, bus = 0, device = 0 -SPI = spidev.SpiDev(0, 0) - -def epd_digital_write(pin, value): - GPIO.output(pin, value) - -def epd_digital_read(pin): - return GPIO.input(BUSY_PIN) - -def epd_delay_ms(delaytime): - time.sleep(delaytime / 1000.0) - -def spi_transfer(data): - SPI.writebytes(data) - -def epd_init(): - GPIO.setmode(GPIO.BCM) - GPIO.setwarnings(False) - GPIO.setup(RST_PIN, GPIO.OUT) - GPIO.setup(DC_PIN, GPIO.OUT) - GPIO.setup(CS_PIN, GPIO.OUT) - GPIO.setup(BUSY_PIN, GPIO.IN) - SPI.max_speed_hz = 2000000 - SPI.mode = 0b00 - return 0; - -### END OF FILE ### diff --git a/raspberry/python/install_inkyphat_lib.sh b/raspberry/python/install_inkyphat_lib.sh new file mode 100644 index 0000000..ae59270 --- /dev/null +++ b/raspberry/python/install_inkyphat_lib.sh @@ -0,0 +1,1118 @@ +#!/bin/bash + +: <<'DISCLAIMER' + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This script is licensed under the terms of the MIT license. +Unless otherwise noted, code reproduced herein +was written for this script. + +- The Pimoroni Crew - + +DISCLAIMER + +# script control variables + +productname="Inky pHAT" # the name of the product to install +scriptname="inkyphat" # the name of this script +spacereq=50 # minimum size required on root partition in MB +debugmode="no" # whether the script should use debug routines +debuguser="none" # optional test git user to use in debug mode +debugpoint="none" # optional git repo branch or tag to checkout +forcesudo="no" # whether the script requires to be ran with root privileges +promptreboot="no" # whether the script should always prompt user to reboot +mininstall="no" # whether the script enforces minimum install routine +customcmd="yes" # whether to execute commands specified before exit +gpioreq="yes" # whether low-level gpio access is required +i2creq="no" # whether the i2c interface is required +i2sreq="no" # whether the i2s interface is required +spireq="yes" # whether the spi interface is required +uartreq="no" # whether uart communication is required +armhfonly="yes" # whether the script is allowed to run on other arch +armv6="yes" # whether armv6 processors are supported +armv7="yes" # whether armv7 processors are supported +armv8="yes" # whether armv8 processors are supported +raspbianonly="no" # whether the script is allowed to run on other OSes +osreleases=( "Raspbian" ) # list os-releases supported +oswarning=( "Debian" "Kano" "PiTop" "RetroPie" ) # list experimental os-releases +osdeny=( "Darwin" "Kali" "Linaro" "Mate" "Ubuntu" "Volumio" ) # list os-releases specifically disallowed +debpackage="inkyphat" # the name of the package in apt repo +piplibname="inkyphat" # the name of the lib in pip repo +pipoverride="yes" # whether the script should give priority to pip repo +pip2support="yes" # whether python2 is supported +pip3support="yes" # whether python3 is supported +topdir="Pimoroni" # the name of the top level directory +localdir="inkyphat" # the name of the dir for copy of resources +gitreponame="inky-phat" # the name of the git project repo +gitusername="pimoroni" # the name of the git user to fetch repo from +gitrepobranch="master" # repo branch to checkout +gitrepotop="root" # the name of the dir to base repo from +gitrepoclone="no" # whether the git repo is to be cloned locally +gitclonedir="source" # the name of the local dir for repo +repoclean="no" # whether any git repo clone found should be cleaned up +repoinstall="no" # whether the library should be installed from repo +libdir="library" # subdirectory of library in repo +copydir=( "examples" ) # subdirectories to copy from repo +copyhead="no" # whether to use the latest repo commit or release tag +pkgremove=() # list of conflicting packages to remove +coredeplist=() # list of core dependencies +pythondep=( "numpy" "pil" ) # list of python dependencies +pipdeplist=() # list of dependencies to source from pypi +examplesdep=( "qrcode" "icalendar" ) # list of python modules required by examples +somemoredep=() # list of additional dependencies +xdisplaydep=() # list of dependencies requiring X server + +# template 1712181300 + +FORCE=$1 +ASK_TO_REBOOT=false +CURRENT_SETTING=false +MIN_INSTALL=false +FAILED_PKG=false +REMOVE_PKG=false +UPDATE_DB=false + +AUTOSTART=~/.config/lxsession/LXDE-pi/autostart +BOOTCMD=/boot/cmdline.txt +CONFIG=/boot/config.txt +DTBODIR=/boot/overlays +APTSRC=/etc/apt/sources.list +INITABCONF=/etc/inittab +BLACKLIST=/etc/modprobe.d/raspi-blacklist.conf +LOADMOD=/etc/modules + +RASPOOL="http://mirrordirector.raspbian.org/raspbian/pool" +RPIPOOL="http://archive.raspberrypi.org/debian/pool" +DEBPOOL="http://ftp.debian.org/debian/pool" +GETPOOL="https://get.pimoroni.com" + +SMBUS2="python-smbus_3.1.1+svn-2_armhf.deb" +SMBUS3="python3-smbus_3.1.1+svn-2_armhf.deb" +SMBUS35="python3-smbus1_1.1+35dbg-1_armhf.deb" + +SPIDEV2="python-spidev_2.0~git20150907_armhf.deb" +SPIDEV3="python3-spidev_2.0~git20150907_armhf.deb" + +RPIGPIO1="raspi-gpio_0.20170105_armhf.deb" +RPIGPIO2="python-rpi.gpio_0.6.3~jessie-1_armhf.deb" +RPIGPIO3="python3-rpi.gpio_0.6.3~jessie-1_armhf.deb" + +export PIP_FORMAT=legacy + +# function define + +confirm() { + if [ "$FORCE" == '-y' ]; then + true + else + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi + fi +} + +prompt() { + read -r -p "$1 [y/N] " response < /dev/tty + if [[ $response =~ ^(yes|y|Y)$ ]]; then + true + else + false + fi +} + +success() { + echo -e "$(tput setaf 2)$1$(tput sgr0)" +} + +inform() { + echo -e "$(tput setaf 6)$1$(tput sgr0)" +} + +warning() { + echo -e "$(tput setaf 1)$1$(tput sgr0)" +} + +newline() { + echo "" +} + +progress() { + count=0 + until [ $count -eq 7 ]; do + echo -n "..." && sleep 1 + ((count++)) + done; + if ps -C $1 > /dev/null; then + echo -en "\r\e[K" && progress $1 + fi +} + +sudocheck() { + if [ $(id -u) -ne 0 ]; then + echo -e "Install must be run as root. Try 'sudo ./$scriptname'\n" + exit 1 + fi +} + +sysclean() { + sudo apt-get clean && sudo apt-get autoclean + sudo apt-get -y autoremove &> /dev/null +} + +sysupdate() { + if ! $UPDATE_DB; then + echo "Updating apt indexes..." && progress apt-get & + sudo apt-get update 1> /dev/null || { warning "Apt failed to update indexes!" && exit 1; } + sleep 3 && UPDATE_DB=true + fi +} + +sysupgrade() { + sudo apt-get upgrade + sudo apt-get clean && sudo apt-get autoclean + sudo apt-get -y autoremove &> /dev/null +} + +sysreboot() { + warning "Some changes made to your system require" + warning "your computer to reboot to take effect." + echo + if prompt "Would you like to reboot now?"; then + sync && sudo reboot + fi +} + +arch_check() { + IS_ARMHF=false + IS_ARMv6=false + + if uname -m | grep -q "armv.l"; then + IS_ARMHF=true + if uname -m | grep -q "armv6l"; then + IS_ARMv6=true + fi + fi +} + +os_check() { + IS_MACOSX=false + IS_RASPBIAN=false + IS_SUPPORTED=false + IS_EXPERIMENTAL=false + OS_NAME="Unknown" + + if uname -s | grep -q "Darwin"; then + OS_NAME="Darwin" && IS_MACOSX=true + elif cat /etc/os-release | grep -q "Kali"; then + OS_NAME="Kali" + elif [ -d ~/.kano-settings ] || [ -d ~/.kanoprofile ]; then + OS_NAME="Kano" + elif whoami | grep -q "linaro"; then + OS_NAME="Linaro" + elif [ -d ~/.config/ubuntu-mate ];then + OS_NAME="Mate" + elif [ -d ~/.pt-os-dashboard ] || [ -d ~/.pt-dashboard ] || [ -f ~/.pt-dashboard-config ]; then + OS_NAME="PiTop" + elif command -v emulationstation > /dev/null; then + OS_NAME="RetroPie" + elif cat /etc/os-release | grep -q "OSMC"; then + OS_NAME="OSMC" + elif cat /etc/os-release | grep -q "volumio"; then + OS_NAME="Volumio" + elif cat /etc/os-release | grep -q "Raspbian"; then + OS_NAME="Raspbian" && IS_RASPBIAN=true + elif cat /etc/os-release | grep -q "Debian"; then + OS_NAME="Debian" + elif cat /etc/os-release | grep -q "Ubuntu"; then + OS_NAME="Ubuntu" + fi + + if [[ " ${osreleases[@]} " =~ " ${OS_NAME} " ]]; then + IS_SUPPORTED=true + fi + if [[ " ${oswarning[@]} " =~ " ${OS_NAME} " ]]; then + IS_EXPERIMENTAL=true + fi +} + +raspbian_check() { + IS_SUPPORTED=false + IS_EXPERIMENTAL=false + + if [ -f /etc/os-release ]; then + if cat /etc/os-release | grep -q "/sid"; then + IS_SUPPORTED=false && IS_EXPERIMENTAL=true + elif cat /etc/os-release | grep -q "stretch"; then + IS_SUPPORTED=true && IS_EXPERIMENTAL=false + elif cat /etc/os-release | grep -q "jessie"; then + IS_SUPPORTED=true && IS_EXPERIMENTAL=false + elif cat /etc/os-release | grep -q "wheezy"; then + IS_SUPPORTED=true && IS_EXPERIMENTAL=false + else + IS_SUPPORTED=false && IS_EXPERIMENTAL=false + fi + fi +} + +home_dir() { + if [ $EUID -ne 0 ]; then + if $IS_MACOSX; then + USER_HOME=$(dscl . -read /Users/$USER NFSHomeDirectory | cut -d: -f2) + else + USER_HOME=$(getent passwd $USER | cut -d: -f6) + fi + else + warning "Running as root, please log in as a regular user with sudo rights!" + echo && exit 1 + fi +} + +space_chk() { + if command -v stat > /dev/null && ! $IS_MACOSX; then + if [ $spacereq -gt $(($(stat -f -c "%a*%S" /)/10**6)) ];then + echo + warning "There is not enough space left to proceed with installation" + if confirm "Would you like to attempt to expand your filesystem?"; then + curl -sS $GETPOOL/expandfs | sudo bash && exit 1 + else + echo && exit 1 + fi + fi + fi +} + +timestamp() { + date +%Y%m%d-%H%M +} + +check_network() { + sudo ping -q -w 10 -c 1 8.8.8.8 | grep "received, 0" &> /dev/null && return 0 || return 1 +} + +launch_url() { + check_network || (error_box "You don't appear to be connected to the internet, please check your connection and try again!" && exit 1) + if command -v xdg-open > /dev/null; then + xdg-open "$1" && return 0 + else + error_box "There was an error attempting to launch your browser!" + fi +} + +get_install() { + check_network || (error_box "You don't appear to be connected to the internet, please check your connection and try again!" && exit 1) + if [ "$1" != diagnostic ];then + sysupdate && UPDATE_DB=true + fi + if ! command -v curl > /dev/null; then + apt_pkg_install "curl" + fi + curl -sS https://get.pimoroni.com/$1 | bash -s - "-y" $2 + read -p "Press Enter to continue..." < /dev/tty +} + +apt_pkg_req() { + APT_CHK=$(dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "install ok installed") + + if [ "" == "$APT_CHK" ]; then + echo "$1 is required" + true + else + echo "$1 is already installed" + false + fi +} + +apt_pkg_install() { + echo "Installing $1..." + sudo apt-get --yes install "$1" 1> /dev/null || { inform "Apt failed to install $1!\nFalling back on pypi..." && return 1; } +} + +apt_deb_chk() { + BEFORE=$(dpkg-query -W "$1" 2> /dev/null) + sudo apt-get --yes install "$1" &> /dev/null || return 1 + AFTER=$(dpkg-query -W "$1" 2> /dev/null) + if [ "$BEFORE" == "$AFTER" ]; then + echo "$1 is already the newest version" + else + echo "$1 was successfully upgraded" + fi +} + +apt_deb_install() { + echo "Installing $1..." + if [[ "$1" != *".deb"* ]]; then + sudo apt-get --yes install "$1" &> /dev/null || inform "Apt failed to install $1!\nFalling back on pypi..." + dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "install ok installed" + else + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` + cd $DEBDIR + wget "$GETPOOL/resources/$1" &> /dev/null + sudo dpkg -i "$DEBDIR/$1" | grep "Installing $1" + fi +} + +pip_cmd_chk() { + if command -v pip2 > /dev/null; then + PIP2_BIN="pip2" + elif command -v pip-2.7 > /dev/null; then + PIP2_BIN="pip-2.7" + elif command -v pip-2.6 > /dev/null; then + PIP2_BIN="pip-2.6" + else + PIP2_BIN="pip" + fi + if command -v pip3 > /dev/null; then + PIP3_BIN="pip3" + elif command -v pip-3.3 > /dev/null; then + PIP3_BIN="pip-3.3" + elif command -v pip-3.2 > /dev/null; then + PIP3_BIN="pip-3.2" + fi +} + +pip2_lib_req() { + PIP2_CHK=$($PIP2_BIN list 2> /dev/null | grep -i "$1") + + if [ -z "$PIP2_CHK" ]; then + true + else + false + fi +} + +pip3_lib_req() { + PIP3_CHK=$($PIP3_BIN list 2> /dev/null | grep -i "$1") + + if [ -z "$PIP3_CHK" ]; then + true + else + false + fi +} + +usb_max_power() { + if grep -q "^max_usb_current=1$" $CONFIG; then + echo -e "\nMax USB current setting already active" + else + echo -e "\nAdjusting USB current setting in $CONFIG" + echo "max_usb_current=1" | sudo tee -a $CONFIG &> /dev/null + fi +} + +add_dtoverlay() { + if grep -q "^dtoverlay=$1" $CONFIG; then + echo -e "\n$1 overlay already active" + elif grep -q "^#dtoverlay=$1" $CONFIG; then + sudo sed -i "/^#dtoverlay=$1$/ s|#||" $CONFIG + echo -e "\nAdding $1 overlay to $CONFIG" + ASK_TO_REBOOT=true + else + echo "dtoverlay=$1" | sudo tee -a $CONFIG &> /dev/null + echo -e "\nAdding $1 overlay to $CONFIG" + ASK_TO_REBOOT=true + fi +} + +remove_dtoverlay() { + sudo sed -i "/^dtoverlay=$1$/ s|^|#|" $CONFIG + ASK_TO_REBOOT=true +} + +enable_pi_audio() { + if grep -q "#dtparam=audio=on" $CONFIG; then + sudo sed -i "/^#dtparam=audio=on$/ s|#||" $CONFIG + echo -e "\nsnd_bcm2835 loaded (on-board audio enabled)" + ASK_TO_REBOOT=true + fi +} + +disable_pi_audio() { + if grep -q "^dtparam=audio=on" $CONFIG; then + sudo sed -i "/^dtparam=audio=on$/ s|^|#|" $CONFIG + echo -e "\nsnd_bcm2835 unloaded (on-board audio disabled)" + ASK_TO_REBOOT=true + fi +} + +test_audio() { + echo + if confirm "Do you wish to test your system now?"; then + echo -e "\nTesting..." + speaker-test -l5 -c2 -t wav + fi +} + +disable_pulseaudio() { + sudo mv /etc/xdg/autostart/pulseaudio.desktop /etc/xdg/autostart/pulseaudio.disabled &> /dev/null + pulseaudio -k &> /dev/null +} + +kill_volumealsa() { + sed -i "s|type=volumealsa|type=space|" $HOME/.config/lxpanel/LXDE/panels/panel &> /dev/null + sed -i "s|type=volumealsa|type=space|" $HOME/.config/lxpanel/LXDE-pi/panels/panel &> /dev/null +} + +basic_asound() { + sudo echo -e "pcm.\041default {\n type hw\n card 1\n}" > $HOME/.asoundrc + sudo echo -e "ctl.\041default {\n type hw\n card 1\n}" >> $HOME/.asoundrc + sudo mv $HOME/.asoundrc /etc/asound.conf +} + +config_set() { + if [ -n $defaultconf ]; then + sudo sed -i "s|$1=.*$|$1=$2|" $defaultconf + else + sudo sed -i "s|$1=.*$|$1=$2|" $3 + fi +} + +servd_trig() { + if command -v service > /dev/null; then + sudo service $1 $2 + fi +} + +get_init_sys() { + if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then + SYSTEMD=1 + elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then + SYSTEMD=0 + else + echo "Unrecognised init system" && exit 1 + fi +} + +i2c_vc_dtparam() { + if [ -e $CONFIG ] && grep -q "^dtparam=i2c_vc=on$" $CONFIG; then + echo -e "\ni2c0 bus already active" + else + echo -e "\nEnabling i2c0 bus in $CONFIG" + echo "dtparam=i2c_vc=on" | sudo tee -a $CONFIG && echo + fi +} + +: <<'MAINSTART' + +Perform all variables declarations as well as function definition +above this section for clarity, thanks! + +MAINSTART + +# intro message + +if [ $debugmode != "no" ]; then + if [ $debuguser != "none" ]; then + gitusername="$debuguser" + fi + if [ $debugpoint != "none" ]; then + gitrepobranch="$debugpoint" + fi + inform "\nDEBUG MODE ENABLED" + echo -e "git user $gitusername and $gitrepobranch branch/tag will be used\n" +else + echo -e "\nThis script will install everything needed to use\n$productname" + if [ "$FORCE" != '-y' ]; then + inform "\nAlways be careful when running scripts and commands copied" + inform "from the internet. Ensure they are from a trusted source.\n" + echo -e "If you want to see what this script does before running it," + echo -e "you should run: 'curl $GETPOOL/$scriptname'\n" + fi +fi + +# checks and init + +arch_check +os_check +space_chk +home_dir + +if [ $debugmode != "no" ]; then + echo "USER_HOME is $USER_HOME" + echo "OS_NAME is $OS_NAME" + echo "IS_SUPPORTED is $IS_SUPPORTED" + echo "IS_EXPERIMENTAL is $IS_EXPERIMENTAL" + echo +fi + +if ! $IS_ARMHF; then + warning "This hardware is not supported, sorry!" + warning "Config files have been left untouched\n" + exit 1 +fi + +if $IS_ARMv8 && [ $armv8 == "no" ]; then + warning "Sorry, your CPU is not supported by this installer\n" + exit 1 +elif $IS_ARMv7 && [ $armv7 == "no" ]; then + warning "Sorry, your CPU is not supported by this installer\n" + exit 1 +elif $IS_ARMv6 && [ $armv6 == "no" ]; then + warning "Sorry, your CPU is not supported by this installer\n" + exit 1 +fi + +if [ $raspbianonly == "yes" ] && ! $IS_RASPBIAN;then + warning "This script is intended for Raspbian on a Raspberry Pi!\n" + exit 1 +fi + +if $IS_RASPBIAN; then + raspbian_check + if ! $IS_SUPPORTED && ! $IS_EXPERIMENTAL; then + warning "\n--- Warning ---\n" + echo "The $productname installer" + echo "does not work on this version of Raspbian." + echo "Check https://github.com/$gitusername/$gitreponame" + echo "for additional information and support" && echo + exit 1 + fi +fi + +if ! $IS_SUPPORTED && ! $IS_EXPERIMENTAL; then + warning "Your operating system is not supported, sorry!\n" + exit 1 +fi + +if $IS_EXPERIMENTAL; then + warning "\nSupport for your operating system is experimental. Please visit" + warning "forums.pimoroni.com if you experience issues with this product.\n" +fi + +if [ $forcesudo == "yes" ]; then + sudocheck +fi + +if [ $uartreq == "yes" ]; then + echo "Note: $productname requires UART communication" + warning "The serial console will be disabled if you proceed!" +fi +if [ $spireq == "yes" ]; then + echo -e "Note: $productname requires SPI communication" +fi +if [ $i2creq == "yes" ]; then + echo -e "Note: $productname requires I2C communication" +fi +if [ $i2sreq == "yes" ]; then + echo -e "Note: $productname uses the I2S interface" + if [ $OS_NAME != "Volumio" ]; then + warning "The on-board audio chip will be disabled if you proceed!" + fi +fi + +newline +if confirm "Do you wish to continue?"; then + +# basic environment preparation + + echo -e "\nChecking environment..." + + if [ "$FORCE" != '-y' ]; then + if ! check_network; then + warning "We can't connect to the Internet, check your network!" && exit 1 + fi + sysupdate && newline + fi + + if apt_pkg_req "apt-utils" &> /dev/null; then + apt_pkg_install "apt-utils" + fi + if ! command -v curl > /dev/null; then + apt_pkg_install "curl" + fi + if ! command -v wget > /dev/null; then + apt_pkg_install "wget" + fi + + if [ "$pip2support" == "yes" ]; then + if ! [ -f "$(which python2)" ]; then + if confirm "Python 2 is not installed. Would like to install it?"; then + progress apt-get & + apt_pkg_install "python-pip" + else + pip2support="na" + fi + elif apt_pkg_req "python-pip" &> /dev/null; then + progress apt-get & + apt_pkg_install "python-pip" + fi + fi + if [ "$pip3support" == "yes" ]; then + if ! [ -f "$(which python3)" ]; then + if prompt "Python 3 is not installed. Would like to install it?"; then + progress apt-get & + apt_pkg_install "python3-pip" + else + pip3support="na" + fi + elif apt_pkg_req "python3-pip" &> /dev/null; then + progress apt-get & + apt_pkg_install "python3-pip" + fi + fi + pip_cmd_chk + +# hardware setup + + echo -e "\nChecking hardware requirements..." + + if [ $uartreq == "yes" ]; then + echo -e "\nThe serial console must be disabled for $productname to work" + curl -sS $GETPOOL/uarton | sudo bash -s - "-y" && ASK_TO_REBOOT=true + fi + + if [ $gpioreq == "yes" ]; then + echo -e "\nChecking for packages required for GPIO control..." + if apt_pkg_req "raspi-gpio"; then + if ! apt_pkg_install "raspi-gpio" &> /dev/null; then + echo "package raspi-gpio can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/r/raspi-gpio/$RPIGPIO1 &> /dev/null + sudo dpkg -i $DEBDIR/$RPIGPIO1 && FAILED_PKG=false + fi + fi + if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-rpi.gpio" &> /dev/null; then + if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then + echo "package python-rpi.gpio can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/r/rpi.gpio/$RPIGPIO2 &> /dev/null + sudo dpkg -i $DEBDIR/$RPIGPIO2 && FAILED_PKG=false + else + sudo $PIP2_BIN install RPi.GPIO && FAILED_PKG=false + fi + fi + if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-rpi.gpio" &> /dev/null; then + if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then + echo "package python3-rpi.gpio can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/r/rpi.gpio/$RPIGPIO3 &> /dev/null + sudo dpkg -i $DEBDIR/$RPIGPIO3 && FAILED_PKG=false + else + sudo $PIP3_BIN install RPi.GPIO && FAILED_PKG=false + fi + fi + if [ "$pip2support" == "yes" ] && [ -f "$(which python2)" ]; then + if ! $PIP2_BIN list | grep "RPi.GPIO" &> /dev/null; then + warning "Unable to install RPi.GPIO for python 2!" && FAILED_PKG=true + else + RPIGPIO2="install ok installed" + fi + fi + if [ "$pip3support" == "yes" ] && [ -f "$(which python3)" ]; then + if ! $PIP3_BIN list | grep "RPi.GPIO" &> /dev/null; then + warning "Unable to install RPi.GPIO for python 3!" && FAILED_PKG=true + else + RPIGPIO3="install ok installed" + fi + fi + if [ "$RPIGPIO2" == "install ok installed" ] || [ "$RPIGPIO3" == "install ok installed" ]; then + if ! $FAILED_PKG; then + echo -e "RPi.GPIO installed and up-to-date" + fi + fi + fi + + if [ $spireq == "yes" ]; then + newline + if ls /dev/spi* &> /dev/null; then + inform "SPI already enabled" + else + echo "SPI must be enabled for $productname to work" + if command -v raspi-config > /dev/null && sudo raspi-config nonint get_spi | grep -q "1"; then + sudo raspi-config nonint do_spi 0 + inform "SPI is now enabled" + else + curl -sS $GETPOOL/spi | sudo bash -s - "-y" && ASK_TO_REBOOT=true + fi + fi + echo -e "\nChecking packages required by SPI interface..." + if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-spidev" &> /dev/null; then + if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then + echo "package python-spidev can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/s/spidev/$SPIDEV2 &> /dev/null + sudo dpkg -i $DEBDIR/$SPIDEV2 && FAILED_PKG=false + else + sudo $PIP2_BIN install spidev && FAILED_PKG=false + fi + fi + if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-spidev" &> /dev/null; then + if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then + echo "package python3-spidev can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/s/spidev/$SPIDEV3 &> /dev/null + sudo dpkg -i $DEBDIR/$SPIDEV3 && FAILED_PKG=false + else + sudo $PIP3_BIN install spidev && FAILED_PKG=false + fi + fi + if [ "$pip2support" == "yes" ] && [ -f "$(which python2)" ]; then + if ! $PIP2_BIN list | grep "spidev" &> /dev/null; then + warning "Unable to install spidev for python 2!" && FAILED_PKG=true + else + SPIDEV2="install ok installed" + fi + fi + if [ "$pip3support" == "yes" ] && [ -f "$(which python3)" ]; then + if ! $PIP3_BIN list | grep "spidev" &> /dev/null; then + warning "Unable to install spidev for python 3!" && FAILED_PKG=true + else + SPIDEV3="install ok installed" + fi + fi + if [ "$SPIDEV2" == "install ok installed" ] || [ "$SPIDEV3" == "install ok installed" ]; then + if ! $FAILED_PKG; then + echo -e "spidev installed and up-to-date" + fi + fi + fi + + if [ $i2creq == "yes" ]; then + newline + if ls /dev/i2c* &> /dev/null; then + inform "I2C already enabled" + else + echo "I2C must be enabled for $productname to work" + if command -v raspi-config > /dev/null && sudo raspi-config nonint get_i2c | grep -q "1"; then + sudo raspi-config nonint do_i2c 0 + inform "I2C is now enabled" + else + curl -sS $GETPOOL/i2c | sudo bash -s - "-y" && ASK_TO_REBOOT=true + fi + fi + echo -e "\nChecking packages required by I2C interface..." + if [ "$pip2support" == "yes" ] && ! apt_pkg_install "python-smbus" &> /dev/null; then + FAILED_PKG=false + if [ -n $(python --version 2>&1 | grep -q "2.7") ]; then + echo "package python-smbus can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/i/i2c-tools/$SMBUS2 &> /dev/null + sudo dpkg -i $DEBDIR/$SMBUS2 || FAILED_PKG=true + fi + if $FAILED_PKG; then + warning "Unable to install smbus for python 2!" + else + echo -e "Python 2 smbus installed and up-to-date" + fi + else + echo -e "Python 2 smbus installed and up-to-date" + fi + if [ "$pip3support" == "yes" ] && ! apt_pkg_install "python3-smbus" &> /dev/null; then + FAILED_PKG=false + if [ -n $(python3 --version 2>&1 | grep -q "3.4") ]; then + echo "package python3-smbus can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $RPIPOOL/main/i/i2c-tools/$SMBUS3 &> /dev/null + sudo dpkg -i $DEBDIR/$SMBUS3 || FAILED_PKG=true + elif [ -n $(python3 --version 2>&1 | grep -q "3.5") ]; then + if apt_pkg_req "python3-smbus1" &> /dev/null; then + echo "package python3-smbus can't be found, fetching from alternative location..." + DEBDIR=`mktemp -d /tmp/pimoroni.XXXXXX` && cd $DEBDIR + wget $GETPOOL/resources/$SMBUS35 &> /dev/null + sudo dpkg -i $DEBDIR/$SMBUS35 || FAILED_PKG=true + fi + fi + if $FAILED_PKG; then + warning "Unable to install smbus for python 3!" + else + echo -e "Python 3 smbus installed and up-to-date" + fi + else + echo -e "Python 3 smbus installed and up-to-date" + fi + fi + + if [ $i2sreq == "yes" ]; then + if [ -f /etc/asound.conf ]; then + sudo rm -f /etc/asound.conf.backup &> /dev/null + sudo mv /etc/asound.conf /etc/asound.conf.backup + inform "existing config backed up to /etc/asound.conf.backup" + fi + if [ -f $HOME/.asoundrc ]; then + sudo rm -f $HOME/.asoundrc.backup &> /dev/null + sudo mv $HOME/.asoundrc $HOME/.asoundrc.backup + inform "existing config backed up to ~/.asound.conf.backup" + fi + fi + +# minimum install routine + + if [ $mininstall != "yes" ] && [ $gitrepoclone != "yes" ]; then + newline + echo "$productname comes with examples and documentation that you may wish to install." + echo "Performing a full install will ensure those resources are installed," + echo "along with all required dependencies. It may however take a while!" + newline + if ! confirm "Do you wish to perform a full install?"; then + MIN_INSTALL=true + fi + else + MIN_INSTALL=true + fi + + if [ $localdir != "na" ]; then + installdir="$USER_HOME/$topdir/$localdir" + else + installdir="$USER_HOME/$topdir" + fi + + if ! $MIN_INSTALL || [ $gitrepoclone == "yes" ]; then + [ -d $installdir ] || mkdir -p $installdir + fi + + if [ $debugmode != "no" ]; then + echo "INSTALLDIR is $installdir" + fi + +# apt repo install + + echo -e "\nChecking for dependencies..." + + if $REMOVE_PKG; then + for pkgrm in ${pkgremove[@]}; do + warning "Installed package conflicts with requirements" + sudo apt-get remove "$pkgrm" + done + fi + + for pkgdep in ${coredeplist[@]}; do + if apt_pkg_req "$pkgdep"; then + apt_pkg_install "$pkgdep" + fi + done + + for pkgdep in ${pythondep[@]}; do + if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then + if apt_pkg_req "python-$pkgdep"; then + apt_pkg_install "python-$pkgdep" + fi + fi + if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then + if apt_pkg_req "python3-$pkgdep"; then + apt_pkg_install "python3-$pkgdep" + fi + fi + done + + if [ $pipoverride != "yes" ] && [ $debpackage != "na" ]; then + newline + if [ $pip2support != "yes" ] && [ $pip3support != "yes" ]; then + apt_deb_install "$debpackage" + fi + if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then + apt_deb_install "python-$debpackage" + if ! apt_pkg_req "python-$debpackage" &> /dev/null; then + sudo $PIP2_BIN uninstall -y "$piplibname" &> /dev/null + fi + fi + if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then + apt_deb_install "python3-$debpackage" + if ! apt_pkg_req "python3-$debpackage" &> /dev/null; then + sudo $PIP3_BIN uninstall -y "$piplibname" &> /dev/null + fi + fi + if apt_pkg_req "python-$debpackage" &> /dev/null || apt_pkg_req "python3-$debpackage" &> /dev/null; then + debpackage="na" + fi + else + debpackage="na" + fi + +# pypi repo install + + if [ -f "$(which python2)" ] && [ $pip2support == "yes" ] && apt_pkg_req "python-$debpackage" &> /dev/null; then + if [ $piplibname != "na" ] && [ $debpackage == "na" ]; then + newline && echo "Installing $productname library for Python 2..." && newline + if ! sudo -H $PIP2_BIN install "$piplibname"; then + warning "Python 2 library install failed!" + echo "If problems persist, visit forums.pimoroni.com for support" + exit 1 + fi + fi + fi + + if [ -f "$(which python3)" ] && [ $pip3support == "yes" ] && apt_pkg_req "python3-$debpackage" &> /dev/null; then + if [ $piplibname != "na" ] && [ $debpackage == "na" ]; then + newline && echo "Installing $productname library for Python 3..." && newline + if ! sudo -H $PIP3_BIN install "$piplibname"; then + warning "Python 3 library install failed!" + echo "If problems persist, visit forums.pimoroni.com for support" + exit 1 + fi + fi + fi + +# git repo install + + if [ $gitrepoclone == "yes" ]; then + if ! command -v git > /dev/null; then + apt_pkg_install git + fi + if [ $gitclonedir == "source" ]; then + gitclonedir=$gitreponame + fi + if [ $repoclean == "yes" ]; then + rm -Rf $installdir/$gitclonedir + fi + if [ -d $installdir/$gitclonedir ]; then + newline && echo "Github repo already present. Updating..." + cd $installdir/$gitclonedir && git pull + else + newline && echo "Cloning Github repo locally..." + cd $installdir + if [ $debugmode != "no" ]; then + echo "git user name is $gitusername" + echo "git repo name is $gitreponame" + fi + if [ $gitrepobranch != "master" ]; then + git clone https://github.com/$gitusername/$gitreponame $gitclonedir -b $gitrepobranch + else + git clone --depth=1 https://github.com/$gitusername/$gitreponame $gitclonedir + fi + fi + fi + + if [ $repoinstall == "yes" ]; then + newline && echo "Installing library..." && newline + cd $installdir/$gitreponame/$libdir + if [ -f "$(which python2)" ] && [ $pip2support == "yes" ]; then + sudo python2 ./setup.py install + fi + if [ -f "$(which python3)" ] && [ $pip3support == "yes" ]; then + sudo python3 ./setup.py install + fi + newline + fi + +# additional install + + if ! $MIN_INSTALL; then + echo -e "\nChecking for additional software..." + for moredep in ${examplesdep[@]}; do + if [ -f "$(which python2)" ] && apt_pkg_req "python-$moredep"; then + if ! apt_pkg_install "python-$moredep"; then + sudo -H $PIP2_BIN install "$moredep" + if pip2_lib_req "$moredep"; then + FAILED_PKG=true + fi + fi + fi + if [ -f "$(which python3)" ] && apt_pkg_req "python3-$moredep"; then + if ! apt_pkg_install "python3-$moredep"; then + sudo -H $PIP3_BIN install "$moredep" + if pip3_lib_req "$moredep"; then + FAILED_PKG=true + fi + fi + fi + done + for pipdep in ${pipdeplist[@]}; do + if [ -f "$(which python2)" ] && pip2_lib_req "$pipdep"; then + sudo -H $PIP2_BIN install "$pipdep" + fi + if [ -f "$(which python3)" ] && pip3_lib_req "$pipdep"; then + sudo -H $PIP3_BIN install "$pipdep" + fi + done + for moredep in ${somemoredep[@]}; do + if apt_pkg_req "$moredep"; then + apt_pkg_install "$moredep" + fi + done + if [ -n "$DISPLAY" ]; then + for x11dep in ${xdisplaydep[@]}; do + if apt_pkg_req "$x11dep"; then + apt_pkg_install "$x11dep" + fi + done + fi + fi + +# resources install + + if ! $MIN_INSTALL && [ -n "$copydir" ]; then + if ! command -v git > /dev/null; then + apt_pkg_install git + fi + echo -e "\nDownloading examples and documentation..." + TMPDIR=`mktemp -d /tmp/pimoroni.XXXXXX` + cd $TMPDIR + if [ $copyhead != "yes" ]; then + GITTAG=$(git ls-remote -t https://github.com/$gitusername/$gitreponame v\?.?.? | tail -n 1 | rev | cut -c -6 | rev) + fi + if [ -n "$GITTAG" ]; then + git clone https://github.com/$gitusername/$gitreponame -b $GITTAG &> /dev/null + else + git clone --depth=1 https://github.com/$gitusername/$gitreponame &> /dev/null + fi + cd $installdir + for repodir in ${copydir[@]}; do + if [ -d $repodir ] && [ $repodir != "documentation" ]; then + newline + if [ -d $installdir/$repodir-backup ]; then + rm -R $installdir/$repodir-old &> /dev/null + mv $installdir/$repodir-backup $installdir/$repodir-old &> /dev/null + fi + mv $installdir/$repodir $installdir/$repodir-backup &> /dev/null + if [ $gitrepotop != "root" ]; then + cp -R $TMPDIR/$gitreponame/$gitrepotop/$repodir $installdir/$repodir &> /dev/null + else + cp -R $TMPDIR/$gitreponame/$repodir $installdir/$repodir &> /dev/null + fi + inform "The $repodir directory already exists on your system!" + echo -e "We've backed them up as $repodir-backup, just in case you've changed anything!\n" + else + rm -R $installdir/$repodir &> /dev/null + if [ $gitrepotop != "root" ]; then + cp -R $TMPDIR/$gitreponame/$gitrepotop/$repodir $installdir/$repodir &> /dev/null + else + cp -R $TMPDIR/$gitreponame/$repodir $installdir/$repodir &> /dev/null + fi + fi + done + echo "Resources for your $productname were copied to" + inform "$installdir" + rm -rf $TMPDIR + fi + +# script custom routines + + if [ $customcmd == "no" ]; then + if [ -n "$pkgremove" ]; then + echo -e "\nFinalising Install...\n" + sysclean && newline + fi + echo -e "\nAll done. Enjoy your $productname!\n" + else # custom block starts here + echo -e "\nFinalising Install...\n" + # place all custom commands in this scope + fi + + if $FAILED_PKG; then + warning "\nSome packages could not be installed, review the output for details!\n" + fi + if $IS_EXPERIMENTAL; then + warning "\nSupport for your operating system is experimental. Please visit" + warning "forums.pimoroni.com if you experience issues with this product.\n" + fi + + if [ "$FORCE" != '-y' ]; then + if [ $promptreboot == "yes" ] || $ASK_TO_REBOOT; then + sysreboot && newline + fi + fi +else + echo -e "\nAborting...\n" +fi + +exit 0 diff --git a/raspberry/python/mqtt2epaper.py b/raspberry/python/mqtt2epaper.py index 12aad0b..0e77a01 100644 --- a/raspberry/python/mqtt2epaper.py +++ b/raspberry/python/mqtt2epaper.py @@ -19,7 +19,7 @@ Display data from MQTT broker on ePaper screen @author: arofarn """ -__version__ = 0.2 +__version__ = 0.3 ########### # IMPORTS # @@ -30,29 +30,20 @@ import paho.mqtt.client as mqtt import time import json import netifaces, socket -import epd2in13 +import inkyphat from PIL import Image from PIL import ImageDraw from PIL import ImageFont +########## +# Config # +########## mqtt_client_id = "epaper_display" # Colors codes -white = 255 -black = 0 - -def update_epd(): - """ - Update ePaper Display - """ - # 90° rotation + 180° if parameter epd_rotate different of 0 - # The small translation corrects an offset after rotation of 270° - screen = image.rotate(90 + (epd_rotate * 180), - expand=True, - translate=(-6*epd_rotate, 0)) - # Put the picture to display in frame memory and display the new frame - epd.set_frame_memory(screen, 0, 0) - epd.display_frame() +white = inkyphat.WHITE +black = inkyphat.BLACK +grey = inkyphat.RED ############# # CALLBACKS # @@ -92,21 +83,23 @@ def on_message(client, userdata, msg): val['unit'] = val['unit'].replace('deg', '°') #List of 'displayable' data types and their coordinate on screen - coord_type = { 'AT' : (12, 50), - 'RH' : (137, 50), - 'AP' : (12, 72), - 'ALTI' : (137, 72), + coord_type = { 'AT' : (1, data1_line), + 'RH' : (inkyphat.WIDTH / 2 + 12, data1_line), + 'AP' : (1, data2_line), + 'ALTI' : (inkyphat.WIDTH / 2 + 12, data2_line), } print(val) if val['type'] in coord_type: #Erase old data - draw.rectangle(coord_type[val['type']] + (coord_type[val['type']][0] + 112, coord_type[val['type']][1] + 20), fill=white) + inkyphat.rectangle(coord_type[val['type']] + (coord_type[val['type']][0] + inkyphat.WIDTH / 2 - 2, + coord_type[val['type']][1] + 16), + fill=white) #Draw new values - draw.text(coord_type[val['type']], - "{:6.1f}{} ".format(val['value'], val['unit']), - font=font20_bold, - fill=black) - update_epd() + inkyphat.text(coord_type[val['type']], + "{:6.1f}{} ".format(val['value'], val['unit']), + font=data_font, + fill=black) + inkyphat.show() # Print feather's date and time on epaper display elif subtopics[0] == "SYS": @@ -121,11 +114,11 @@ def on_message(client, userdata, msg): dt_texte = payload print(dt_texte) # First erase old text - draw.rectangle((0, 34, epd2in13.EPD_HEIGHT, 48), fill=white) + inkyphat.rectangle((0, 31, inkyphat.WIDTH, 45), fill=white) # and draw the new date - draw.text((0, 34), dt_texte, font=font14, fill=black) + inkyphat.text((0, 31), dt_texte, font=font12, fill=black) - update_epd() + inkyphat.show() def on_message_camera(client, userdata, msg): """Update display with info from camera (camera shooting new picture or name @@ -136,15 +129,15 @@ def on_message_camera(client, userdata, msg): # If a picture is been taken if pl == "1" and msg.topic == camera_mqtt_topic + "/shooting" : - draw.rectangle((0, 106, epd2in13.EPD_HEIGHT, epd2in13.EPD_WIDTH), fill=white) - draw.text((0, 106), "Shooting photo... ", font=font14, fill=black) - update_epd() + inkyphat.rectangle((0, inkyphat.HEIGHT - 13, inkyphat.WIDTH, inkyphat.HEIGHT), fill=white) + inkyphat.text((0, inkyphat.HEIGHT - 13), "Shooting photo... ", font=font12, fill=black) + inkyphat.show() #Last picture name (with date/time) if msg.topic == camera_mqtt_topic + "/last_photo": - draw.rectangle((0, 106, epd2in13.EPD_HEIGHT, epd2in13.EPD_WIDTH), fill=white) - draw.text((0, 106), "Last: " + pl, font=font14, fill=black) - update_epd() + inkyphat.rectangle((0, inkyphat.HEIGHT - 12, inkyphat.WIDTH, inkyphat.HEIGHT), fill=white) + inkyphat.text((0, inkyphat.HEIGHT - 12), "Last: " + pl, font=font12, fill=black) + inkyphat.show() def on_disconnect(client, userdata, msg): @@ -158,51 +151,52 @@ def on_disconnect(client, userdata, msg): ######## #Screen init. -epd = epd2in13.EPD() -epd.init(epd.lut_full_update) +inkyphat.set_colour("black") #List of TrueType fonts -font12 = ImageFont.truetype(epd_font_file, 12) -font14 = ImageFont.truetype(epd_font_file, 14) -font16 = ImageFont.truetype(epd_font_file, 16) -font20 = ImageFont.truetype(epd_font_file, 20) -font12_bold = ImageFont.truetype(epd_bold_font_file, 12) -font14_bold = ImageFont.truetype(epd_bold_font_file, 14) -font16_bold = ImageFont.truetype(epd_bold_font_file, 16) -font20_bold = ImageFont.truetype(epd_bold_font_file, 20) -#font12_obl = ImageFont.truetype(epd_italic_font_file, 12) -#font14_obl = ImageFont.truetype(epd_italic_font_file, 14) -#font16_obl = ImageFont.truetype(epd_italic_font_file, 16) +font10 = inkyphat.ImageFont.truetype(epd_font_file, 10) +font11 = inkyphat.ImageFont.truetype(epd_font_file, 11) +font12 = inkyphat.ImageFont.truetype(epd_font_file, 12) +font14 = inkyphat.ImageFont.truetype(epd_font_file, 14) +font16 = inkyphat.ImageFont.truetype(epd_font_file, 16) +font20 = inkyphat.ImageFont.truetype(epd_font_file, 20) +font12_bold = inkyphat.ImageFont.truetype(epd_bold_font_file, 12) +font14_bold = inkyphat.ImageFont.truetype(epd_bold_font_file, 14) +font16_bold = inkyphat.ImageFont.truetype(epd_bold_font_file, 16) +font20_bold = inkyphat.ImageFont.truetype(epd_bold_font_file, 20) +#font12_obl = inkyphat.ImageFont.truetype(epd_italic_font_file, 12) +#font14_obl = inkyphat.ImageFont.truetype(epd_italic_font_file, 14) +#font16_obl = inkyphat.ImageFont.truetype(epd_italic_font_file, 16) -#New image buffer -# Memo : 0=black, 255= white/blank -image = Image.new('1', (epd2in13.EPD_HEIGHT, epd2in13.EPD_WIDTH), 255) -draw = ImageDraw.Draw(image) +# Lines and fonts +title_line = 1 +title_font = font14_bold +hostname_line = 19 +hostname_font = font11 +date_line = 31 +date_font = font12 +data1_line = 44 +data2_line = 62 +data_font = font16_bold +data_table_bottom_line = data2_line + 17 #Draw title in white on black -draw.rectangle((0, 0, epd2in13.EPD_HEIGHT, 18), fill=black) -draw.text((60, 1), - "Projet Camétéo", - font=font16_bold, - fill=white) +inkyphat.rectangle((0, 0, inkyphat.WIDTH, 17), fill=grey) +inkyphat.text((60, 1), "Projet Camétéo", font=title_font, fill=white) + #Write hostname and IP address -draw.text((0, 19), - "{} : IP= {}".format(socket.gethostname(), - netifaces.ifaddresses('wlan0')[2][0]['addr']), - font = font12, - fill=black ) +inkyphat.text((0, 19), + "{} : {}".format(socket.gethostname(), + netifaces.ifaddresses('wlan0')[2][0]['addr']), + font = hostname_font, + fill = black ) # Draw the black lines for data table -draw.line((0,49,epd2in13.EPD_HEIGHT, 49), fill=black) -draw.line((0,71,epd2in13.EPD_HEIGHT, 71), fill=black) -draw.line((0,93,epd2in13.EPD_HEIGHT, 93), fill=black) -draw.line((125, 49, 125, 93), fill=black) +inkyphat.line((0, data1_line - 1, inkyphat.WIDTH, data1_line - 1), fill=black) +inkyphat.line((0, data2_line - 1, inkyphat.WIDTH, data2_line - 1), fill=black) +inkyphat.line((0, data_table_bottom_line, inkyphat.WIDTH, data_table_bottom_line), fill=black) +inkyphat.line((inkyphat.WIDTH / 2, data1_line - 1, inkyphat.WIDTH / 2, data_table_bottom_line), fill=black) -#Display on ePaper screen -epd.clear_frame_memory(0xFF) -update_epd() - -#Toggle to partial refresh -epd.init(epd.lut_partial_update) +inkyphat.show() #Connect to MQTT broker and loop... mqtt_client = mqtt.Client(mqtt_client_id, clean_session=True)