diff --git a/default_settings.sh b/default_settings.sh new file mode 100644 index 0000000..5af7217 --- /dev/null +++ b/default_settings.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +export INSTALL_DOCS=true +export INSTALL_ROS=true +export INSTALL_ARDUINO=true +export INSTALL_WEB=true +export INSTALL_PYTHON=true +export INSTALL_JUPYTER=true +export EXPIRE_PASSWD=true +export INSTALL_NETWORK=true +export INSTALL_PROVISIONING=false +export INSTALL_VSCODE=true +export INSTALL_PAM=true +export EXTRA_SCRIPTS=( + # "testExtra.sh" + # "testExtra2.sh" +) +export PARALLEL=true diff --git a/download_repos.sh b/download_repos.sh index 1f9139e..867eafe 100755 --- a/download_repos.sh +++ b/download_repos.sh @@ -7,6 +7,7 @@ sudo apt-get update sudo apt-get install -y python3-vcstool # Download all Mirte repositories +ls -alh vcs import --workers 1 /etc/apt/sources.list.d/ros-latest.list' curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - -sudo apt update +until sudo apt update; do + echo "retrying apt update in 1s" + sleep 1 +done sudo apt install -y ros-noetic-ros-base python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential python3-catkin-tools python3-osrf-pycommon grep -qxF "source /opt/ros/noetic/setup.bash" /home/mirte/.bashrc || echo "source /opt/ros/noetic/setup.bash" >>/home/mirte/.bashrc source /opt/ros/noetic/setup.bash @@ -64,7 +70,7 @@ sudo pip3 install adafruit-circuitpython-busdevice==5.1.1 adafruit-circuitpython sudo pip3 install pillow adafruit-circuitpython-ssd1306==2.12.1 # Install aio dependencies -sudo pip3 install janus async-generator nest-asyncio +sudo pip3 install janus async-generator nest-asyncio catkin_pkg git clone https://github.com/locusrobotics/aiorospy.git cd aiorospy/aiorospy || exit 1 sudo pip3 install . diff --git a/install_arduino.sh b/install_arduino.sh index aa6b480..9cb54e3 100755 --- a/install_arduino.sh +++ b/install_arduino.sh @@ -4,57 +4,36 @@ MIRTE_SRC_DIR=/usr/local/src/mirte # Install dependencies sudo apt install -y git curl binutils libusb-1.0-0 - -# Install arduino-cli -# We need to install version 0.13.0. From version 0.14.0 on a check is done on the hash of the packages, -# while the community version of the STM (see below) needs insecure packages. -curl https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sudo BINDIR=/usr/local/bin sh -s 0.13.0 - -# Install arduino avr support (for nano) -arduino-cli -v core update-index --additional-urls https://raw.githubusercontent.com/koendv/stm32duino-raspberrypi/master/BoardManagerFiles/package_stm_index.json -arduino-cli -v core install arduino:avr - -# Install STM32 support. Currently not supported by stm32duino (see https://github.com/stm32duino/Arduino_Core_STM32/issues/708), but there is already -# a community version (https://github.com/koendv/stm32duino-raspberrypi). TODO: go back to stm32duino as soon as it is merged into stm32duino. -arduino-cli -v core install STM32:stm32 --additional-urls https://github.com/koendv/stm32duino-raspberrypi/blob/v1.3.2-4/BoardManagerFiles/package_stm_index.json -#arduino-cli -v core install STM32:stm32 --additional-urls https://github.com/zoef-robot/stm32duino-raspberrypi/master/BoardManagerFiles/package_stm_index.json - -# Fix for community STM32 (TODO: make version independant) -sed -i 's/dfu-util\.sh/dfu-util\/dfu-util/g' /home/mirte/.arduino15/packages/STM32/tools/STM32Tools/1.4.0/tools/linux/maple_upload -ln -s /home/mirte/.arduino15/packages/STM32/tools/STM32Tools/1.4.0/tools/linux/maple_upload /home/mirte/.arduino15/packages/STM32/tools/STM32Tools/1.4.0/tools/linux/maple_upload.sh -sudo cp /home/mirte/.arduino15/packages/STM32/tools/STM32Tools/1.4.0/tools/linux/45-maple.rules /etc/udev/rules.d/45-maple.rules -# Retartsing should only be done when not in qemu -#sudo service udev restart - -# Install libraries needed by FirmataExpress -arduino-cli lib install "NewPing" -arduino-cli lib install "Stepper" -arduino-cli lib install "Servo" -arduino-cli lib install "DHTNEW" - -# Install our own arduino libraries -ln -s $MIRTE_SRC_DIR/mirte-arduino-libraries/OpticalEncoder /home/mirte/Arduino/libraries - -# Install Blink example code -mkdir /home/mirte/arduino_project/Blink -ln -s $MIRTE_SRC_DIR/mirte-install-scripts/Blink.ino /home/mirte/arduino_project/Blink - -# Already build all versions so only upload is needed -./run_arduino.sh build Telemetrix4Arduino -./run_arduino.sh build_nano Telemetrix4Arduino -./run_arduino.sh build_nano_old Telemetrix4Arduino -./run_arduino.sh build_uno Telemetrix4Arduino - -# Add mirte to dialout -sudo adduser mirte dialout - -# By default, armbian has ssh login for root enabled with password 1234. -# The password need to be set to mirte_mirte so users can use the -# Arduino IDE remotely. -# TODO: when the Arduino IDE also supports ssh for non-root-users -# this has to be changed -echo -e "mirte_mirte\nmirte_mirte" | sudo passwd root - -# Enable tuploading from remote IDE -sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/run-avrdude /usr/bin -sudo bash -c 'echo "mirte ALL = (root) NOPASSWD: /usr/local/bin/arduino-cli" >> /etc/sudoers' +ls -alh $MIRTE_SRC_DIR +pip3 install -U platformio +echo "export PATH=$PATH:/home/mirte/.local/bin" >/home/mirte/.bashrc +export PATH=$PATH:/home/mirte/.local/bin +curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules + +mkdir -p /home/mirte/Arduino/Telemetrix4Arduino +ls $MIRTE_SRC_DIR/mirte-telemetrix4arduino -al +ln -s $MIRTE_SRC_DIR/mirte-telemetrix4arduino /home/mirte/Arduino/Telemetrix4Arduino || true +cd $MIRTE_SRC_DIR/mirte-telemetrix4arduino || exit +pio run -e robotdyn_blackpill_f303cc -e nanoatmega328new -e nanoatmega328 + +# pico stuff +sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential libusb-1.0-0-dev libstdc++-arm-none-eabi-newlib -y +cd $MIRTE_SRC_DIR/pico-sdk/ || exit +git submodule update --init +echo "export PICO_SDK_PATH=$MIRTE_SRC_DIR/pico-sdk/" >/home/mirte/.bashrc +export PICO_SDK_PATH=$MIRTE_SRC_DIR/pico-sdk/ +cd $MIRTE_SRC_DIR/mirte-telemetrix4rpipico || exit +mkdir build +cd build || exit +cmake .. +make + +# TODO: add picotool and install it: +# git clone https://github.com/raspberrypi/picotool.git --branch master --progress +# cd picotool +# mkdir build +# cd build +# export PICO_SDK_PATH=~/pico/pico-sdk +# cmake ../ +# make +# make install diff --git a/install_docs.sh b/install_docs.sh new file mode 100644 index 0000000..8eb7c03 --- /dev/null +++ b/install_docs.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +cd $MIRTE_SRC_DIR/mirte-documentation || exit +sudo apt install -y python3.8-venv libenchant-dev +python3 -m venv docs-env +source docs-env/bin/activate +pip install -r requirements.txt +mkdir -p _modules/catkin_ws/src +cd _modules || exit +ls +ln -s $MIRTE_SRC_DIR/mirte-python . +ls +cd mirte-python || exit +pip install . +source /opt/ros/noetic/setup.bash +source /home/mirte/mirte_ws/devel/setup.bash +cd ../../ +make html +deactivate diff --git a/install_jupyter_ros.sh b/install_jupyter_ros.sh index 4eac751..f2b5f38 100755 --- a/install_jupyter_ros.sh +++ b/install_jupyter_ros.sh @@ -29,7 +29,13 @@ deactivate sudo chown -R mirte:mirte /home/mirte/jupyter # TEMP: download examples -git clone https://github.com/RoboStack/jupyter-ros.git +if [ "$UPDATE" ]; then + cd /home/mirte/jupyter-ros || exit + git pull +else + git clone https://github.com/RoboStack/jupyter-ros.git +fi + sudo chown -R mirte:mirte /home/mirte/jupyter-ros # Add systemd service to start jupyter diff --git a/install_mirte.sh b/install_mirte.sh index 5f18c91..89bec56 100755 --- a/install_mirte.sh +++ b/install_mirte.sh @@ -2,6 +2,31 @@ set -xe MIRTE_SRC_DIR=/usr/local/src/mirte +. $MIRTE_SRC_DIR/settings.sh || ( + export INSTALL_DOCS=true + export INSTALL_ROS=true + export INSTALL_ARDUINO=true + export INSTALL_WEB=true + export BUILD_WEB=true + export INSTALL_PYTHON=true + export INSTALL_JUPYTER=true + export EXPIRE_PASSWD=true + export INSTALL_NETWORK=true + export INSTALL_PROVISIONING=true + export PARALLEL=true + +) + +wait_all() { + while [ "$(jobs -p | wc -l)" -gt 0 ]; do # wait for all backgrounded jobs to finish + state=0 + wait -n || state=$? # wait for next job to finish. "||" is required to not trigger set -e + if [[ $state -ne 0 ]]; then + pkill -P $$ || true # kill all child processes + exit 1 # exit with error + fi + done +} # disable ipv6, as not all package repositories are available over ipv6 sudo tee /etc/apt/apt.conf.d/99force-ipv4 <&1 | sed -u 's/^/locales::: /' & +if ! $PARALLEL; then + wait_all +fi # Install vcstool cp repos.yaml $MIRTE_SRC_DIR cp download_repos.sh $MIRTE_SRC_DIR || true cd $MIRTE_SRC_DIR || exit 1 -./download_repos.sh +. ./download_repos.sh -# Install dependecnies to be able to run python3.8 +# Install dependencies to be able to run python3.8 sudo apt install -y python3.8 python3-pip python3-setuptools pip3 install setuptools --upgrade # Set piwheels as pip repo sudo bash -c "echo '[global]' > /etc/pip.conf" sudo bash -c "echo 'extra-index-url=https://www.piwheels.org/simple' >> /etc/pip.conf" -# Install telemetrix -cd $MIRTE_SRC_DIR/mirte-telemetrix-aio || exit 1 -pip3 install . -cd $MIRTE_SRC_DIR/mirte-tmx-pico-aio || exit 1 -pip3 install . - -# Install Telemtrix4Arduino project -# TODO: building STM sometimes fails (and/or hangs) -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -mkdir -p /home/mirte/Arduino/libraries -mkdir -p /home/mirte/arduino_project/Telemetrix4Arduino -ln -s $MIRTE_SRC_DIR/mirte-telemetrix4arduino /home/mirte/Arduino/libraries/Telemetrix4Arduino -ln -s $MIRTE_SRC_DIR/mirte-telemetrix4arduino/examples/Telemetrix4Arduino/Telemetrix4Arduino.ino /home/mirte/arduino_project/Telemetrix4Arduino +if $INSTALL_ROS; then + { + # Install telemetrix + cd $MIRTE_SRC_DIR/mirte-telemetrix-aio || exit 1 + pip3 install . + cd $MIRTE_SRC_DIR/mirte-tmx-pico-aio || exit 1 + pip3 install . + echo "done telemetrix" + } 2>&1 | sed -u 's/^/telemetrix::: /' & +fi +if ! $PARALLEL; then + wait_all +fi +if $INSTALL_ARDUINO; then + { + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -# Install arduino firmata upload script -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_arduino.sh + . ./install_arduino.sh + echo "done arduino" + } 2>&1 | sed -u 's/^/arduino::: /' & +fi +if ! $PARALLEL; then + wait_all +fi +if $INSTALL_PYTHON; then + + { + # Install Mirte Python package + cd $MIRTE_SRC_DIR/mirte-python || exit 1 + pip3 install . + echo "done mirte-python" + } 2>&1 | sed -u 's/^/mirte-python::: /' & +fi +if ! $PARALLEL; then + wait_all +fi +if $INSTALL_WEB; then + + { + # Install Mirte Interface + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + . ./install_web.sh + echo "done web" + } 2>&1 | sed -u 's/^/web::: /' & +fi +if ! $PARALLEL; then + wait_all +fi +if $INSTALL_JUPYTER; then + + { + if [[ ${type:=""} != "mirte_orangepizero" ]]; then + # Install Jupyter Notebook + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + . ./install_jupyter_ros.sh || true # jupyter install fails on orange pi zero 1 + fi + echo "done jupyter_ros" + } 2>&1 | sed -u 's/^/jupyter_ros::: /' & +fi +if ! $PARALLEL; then + wait_all +fi -# Install Mirte Python package -cd $MIRTE_SRC_DIR/mirte-python || exit 1 -pip3 install . +if $INSTALL_VSCODE; then + { + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + . ./install_vscode.sh || exit 1 + echo "done VSCode" + } 2>&1 | sed -u 's/^/vscode::: /' & +fi +if ! $PARALLEL; then + wait_all +fi -# Install Mirte Interface -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_web.sh +if $INSTALL_DOCS; then -if [[ ${type:=""} != "mirte_orangepizero" ]]; then - # Install Jupyter Notebook - cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 - ./install_jupyter_ros.sh || true # jupyter install fails on orange pi zero 1 + # Install Mirte documentation + { + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + . ./install_docs.sh || true # docs building is a bit flaky + echo "done docs" + } 2>&1 | sed -u 's/^/docs::: /' & +fi +if ! $PARALLEL; then + wait_all +fi +if $INSTALL_PROVISIONING; then + + # Install Mirte provisioning system + { + sudo pip install watchdog pyyaml nmcli + sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/provisioning/mirte-provisioning.service /lib/systemd/system/ + sudo systemctl enable mirte-provisioning.service + echo "done provisioning" + } 2>&1 | sed -u 's/^/provisioning::: /' & +fi +if ! $PARALLEL; then + wait_all fi -# Install Mirte ROS packages -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_ROS.sh +if $INSTALL_ROS; then + wait_all # rosdep doesnt wait for other apt scripts to finish, then it just fails installing. If we wait for the others to finish, there won't be parralel apt scripts running. + { + # Install Mirte ROS packages + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + . ./install_ROS.sh + echo "done ROS" + } 2>&1 | sed -u 's/^/ROS::: /' & + wait_all +fi +# Install overlayfs and make sd card read only (software) +# sudo apt install -y overlayroot +# Currently only instaling, not enabled +#sudo bash -c "echo 'overlayroot=\"tmpfs\"' >> /etc/overlayroot.conf" # Install numpy pip3 install numpy - -# Install bluetooth cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_bt.sh - -# Install Mirte documentation -cd $MIRTE_SRC_DIR/mirte-documentation || exit 1 -sudo apt install -y python3.8-venv libenchant-dev -python3 -m venv docs-env -source docs-env/bin/activate -pip install docutils==0.16.0 sphinx-tabs==3.2.0 #TODO: use files to freeze versions -pip install wheel sphinx sphinx-prompt sphinx-rtd-theme sphinxcontrib-spelling sphinxcontrib-napoleon -mkdir -p _modules/catkin_ws/src -cd _modules || exit 1 -ln -s $MIRTE_SRC_DIR/mirte-python . || true -cd mirte-python || exit 1 -pip install . || true -source /opt/ros/noetic/setup.bash -source /home/mirte/mirte_ws/devel/setup.bash -cd ../../ -make html || true -deactivate - -./install_vscode.sh +. ./install_bt.sh || exit 1 # install audio support to use with mirte-pioneer pcb and orange pi zero 2 sudo apt install pulseaudio libasound2-dev libespeak1 -y @@ -104,3 +191,13 @@ sudo apt install -y overlayroot # remove force ipv4 sudo rm /etc/apt/apt.conf.d/99force-ipv4 || true + +echo "Cleaning cache" +sudo du -sh /var/cache/apt/archives +sudo apt clean + +echo "Waiting" +time wait_all # wait on all the backgrounded stuff +echo "Done installing" +# cd /home/mirte/ +date >install_date.txt diff --git a/install_web.sh b/install_web.sh index ae9a7cd..862c673 100755 --- a/install_web.sh +++ b/install_web.sh @@ -10,20 +10,20 @@ sudo apt install -y python3-pip python3-setuptools python3-wheel sudo -H pip install nodeenv # Install nodeenv -nodeenv --node=16.2.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env +sudo nodeenv --node=16.2.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env # Install web interface . $MIRTE_SRC_DIR/mirte-web-interface/node_env/bin/activate - -# Install frontend -cd $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend || exit 1 -npm install . -npm run build - -# Install backend -cd $MIRTE_SRC_DIR/mirte-web-interface/nodejs-backend || exit 1 -npm install . - +if $BUILD_WEB; then + # Install frontend + cd $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend || exit 1 + npm install . + npm run build + + # Install backend + cd $MIRTE_SRC_DIR/mirte-web-interface/nodejs-backend || exit 1 + npm install . +fi # Install wetty #cd $MIRTE_SRC_DIR/mirte-web-interface #npm -g install wetty diff --git a/network_install.sh b/network_install.sh index 64ab2c2..97bbe1c 100755 --- a/network_install.sh +++ b/network_install.sh @@ -1,12 +1,10 @@ #!/bin/bash set -xe MIRTE_SRC_DIR=/usr/local/src/mirte - # Make sure there are no conflicting hcdp-servers sudo apt install -y dnsmasq-base systemctl disable hostapd sed -i 's/#DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf - # Install netplan (not installed on armbian) and networmanager (not installed by Raspberry) sudo apt install -y netplan.io sudo apt install -y network-manager @@ -18,7 +16,6 @@ sudo apt purge -y ifupdown # For the installation we need 8.8.8.8, but linking will be done in network_setup.sh sudo rm -rf /etc/resolv.conf sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf' - # Install wifi-connect MY_ARCH=$(arch) if [[ $MY_ARCH == "armv7l" ]]; then MY_ARCH="armv7hf"; fi @@ -27,7 +24,6 @@ unzip -h || sudo apt install -y unzip unzip wifi-connect* sudo mv wifi-connect /usr/local/sbin rm wifi-connect* - # Added systemd service to account for fix: https://askubuntu.com/questions/472794/hostapd-error-nl80211-could-not-configure-driver-mode sudo rm /lib/systemd/system/mirte-ap.service || true sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ap.service /lib/systemd/system/ @@ -36,7 +32,6 @@ sudo systemctl daemon-reload sudo systemctl stop mirte-ap || /bin/true sudo systemctl start mirte-ap sudo systemctl enable mirte-ap - # Added systemd service to check on boot error for OPi sudo rm /lib/systemd/system/mirte-wifi-watchdog.service || true sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-wifi-watchdog.service /lib/systemd/system/ @@ -45,21 +40,16 @@ sudo systemctl daemon-reload sudo systemctl stop mirte-wifi-watchdog || /bin/true sudo systemctl start mirte-wifi-watchdog sudo systemctl enable mirte-wifi-watchdog - # Install avahi sudo apt install -y libnss-mdns sudo apt install -y avahi-utils avahi-daemon sudo apt install -y avahi-utils avahi-daemon # NOTE: Twice, since regular apt installation on armbian fails (https://forum.armbian.com/topic/10204-cant-install-avahi-on-armbian-while-building-custom-image/) - # Disable lo interface for avahi sed -i 's/#deny-interfaces=eth1/deny-interfaces=lo/g' /etc/avahi/avahi-daemon.conf - # Install dependecies needed for setup script sudo apt install -y inotify-tools wireless-tools - # Disable ssh root login sed -i 's/#PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config - # Install usb_ethernet script from EV3 wget https://raw.githubusercontent.com/ev3dev/ev3-systemd/ev3dev-buster/scripts/ev3-usb.sh -P $MIRTE_SRC_DIR/mirte-install-scripts sudo chmod +x $MIRTE_SRC_DIR/mirte-install-scripts/ev3-usb.sh @@ -88,3 +78,11 @@ sudo bash -c "echo 'match-device=driver:wlan0' >> /etc/NetworkManager/NetworkMan # panic the kernel (at boot). Instead of waiting an unkown # time and reboot manually, we will reboot automatically sudo bash -c 'echo "kernel.panic = 10" > /etc/sysctl.conf' + +# separate service for usb ethernet +sudo rm /lib/systemd/system/mirte-usb.service || true +sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-usb.service /lib/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl stop mirte-usb || /bin/true +sudo systemctl start mirte-usb +sudo systemctl enable mirte-usb diff --git a/provisioning/machine_config.py b/provisioning/machine_config.py new file mode 100644 index 0000000..fba4c34 --- /dev/null +++ b/provisioning/machine_config.py @@ -0,0 +1,136 @@ +import yaml +import os +from deepdiff import DeepDiff +import nmcli +import asyncio + +prev_config_file = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "store/machine_config.yaml" +) +print(prev_config_file) + +hostname = "Mirte-XXXXX" + + +def start(mount_point, loop): + config_file = f"{mount_point}/machine_config.yaml" + if not os.path.isfile(config_file): + print("No machine_config configuration, stopping config provisioning") + write_back_configuration({}, config_file) + return + + with open(config_file, "r") as file: + configuration = yaml.safe_load(file) + with open(prev_config_file, "r") as file: + prev_configuration = yaml.safe_load( + file + ) # this file should have all the configuration options + configuration = {**prev_configuration, **configuration} + if "hostname" in configuration: + set_hostname(configuration["hostname"], prev_configuration["hostname"]) + if "access_points" in configuration: + access_points(configuration, loop) + if "password" in configuration: + set_password( + configuration["password"], prev_configuration["password"] + ) # todo: do only when not already done + write_back_configuration(configuration, config_file) + store_prev_config(configuration, prev_config_file) + + +def access_points(configuration, loop): + print(configuration) + try: + for ap in configuration["access_points"]: + print(ap) + loop.create_task(ap_loop(configuration)) + + except Exception as e: + print(e) + + +stopped = False + + +async def stop(): + global stopped + stopped = True + + +# Nmcli can only connect to a network that is in the air, so we need to continuously check available networks and if not connected, try any known connections +async def ap_loop(configuration): + while not stopped: + await asyncio.sleep(10) + await check_ap(configuration) + + +async def check_ap(configuration): + connections = nmcli.connection() + wifi_conn = list( + filter( + lambda conn: conn.conn_type == "wifi" and conn.device != "--", connections + ) + ) + if len(wifi_conn) > 0: + connection = wifi_conn[0] + if connection.name != hostname: # we have a connection to a wifi point + print("existing wifi connection") + return + # No connection or own hotspot + aps = nmcli.device.wifi() + aps = list(map(lambda ap: ap.ssid, aps)) + # known_aps = list(map(lambda ap: ap.ssid, )) + existing_known_aps = list( + filter(lambda known_ap: known_ap["ssid"] in aps, configuration["access_points"]) + ) + # keep ordering of known aps + if len(existing_known_aps) > 0: + ap = existing_known_aps[0] + print(f"connecting to {ap}") + nmcli.device.wifi_connect(ap["ssid"], ap["password"]) + + +def set_hostname(new_hostname, curr_set_hostname): + global hostname + if new_hostname == curr_set_hostname: + return + with open("/etc/hostname", "r") as file: + old_name = file.readlines()[0].strip() + hostname = old_name + if new_hostname == old_name: + return + print(f"Renaming from {old_name} to {new_hostname}") + with open("/etc/hostname", "w") as file: + file.writelines(f"{new_hostname}\n") + hostname = new_hostname + + +def set_password(new_password, prev_set_password): + if new_password == prev_set_password: + # No need to update it and possibly the user edited it already by using the passwd command + return + if ( + len(new_password) < 1 + ): # when changing as the mirte user, there are some checks, when changing as root, no checks + return + print(f'Changing password to "{new_password}"') + o = os.system(f'sudo chpasswd mirte:{new_password}') + print(o) + + +def write_back_configuration(configuration, config_file): + # read back in the hostname file, if not set in this run, then the user can know the hostname after a first boot + with open("/etc/hostname", "r") as file: + current_name = file.readlines()[0].strip() + # if XXXXX, then network setup did not set the hostname yet + if current_name != "Mirte-XXXXXX": + configuration["hostname"] = current_name + config_text = yaml.dump(configuration) + with open(config_file, "w") as file: + file.writelines(config_text) + + +def store_prev_config(configuration, prev_config_file): + config_text = yaml.dump(configuration) + with open(prev_config_file, "w") as file: + file.writelines(config_text) diff --git a/provisioning/mirte-provisioning.service b/provisioning/mirte-provisioning.service new file mode 100644 index 0000000..fc4f1a6 --- /dev/null +++ b/provisioning/mirte-provisioning.service @@ -0,0 +1,8 @@ +[Unit] +Description=Mirte provisioning service +After=multi-user.target +[Service] +Type=simple +ExecStart=/usr/bin/python3 /usr/local/src/mirte/mirte-install-scripts/provisioning/provisioning.py +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/provisioning/provisioning.py b/provisioning/provisioning.py new file mode 100644 index 0000000..e13c24c --- /dev/null +++ b/provisioning/provisioning.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +import time +import asyncio +import traceback +from signal import SIGINT, SIGTERM + +# Provisioning system for the Mirte sd cards. +# Only activate this service when you want to copy configurations from the second partition to the operating system + +# assume mounting point is /mnt/mirte, otherwise change it here to somewhere to let the modules take out the required info +mount_point = "/mnt/mirte/" + +import robot_config +import machine_config +import ssh + +modules = [robot_config, machine_config, ssh] + + +async def stop(event_loop): + for module in modules: + try: + await module.stop() + except Exception as e: + print(e) + event_loop.stop() + + + + +if __name__ == "__main__": + event_loop = asyncio.get_event_loop() + + for module in modules: + try: + module.start(mount_point, event_loop) + except Exception as e: + print(e) + print(traceback.format_exc()) + for signal in [SIGINT, SIGTERM]: + event_loop.add_signal_handler(signal, + lambda: event_loop.create_task(stop(event_loop))) + + + event_loop.run_forever() + + + + + pending = asyncio.all_tasks(loop=event_loop) + event_loop.run_until_complete(asyncio.gather(*pending)) + event_loop.close() \ No newline at end of file diff --git a/provisioning/robot_config.py b/provisioning/robot_config.py new file mode 100644 index 0000000..7edf5de --- /dev/null +++ b/provisioning/robot_config.py @@ -0,0 +1,80 @@ +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler +import os +import shutil +import time + +observer = None + +tmx_config_path = "/usr/local/src/mirte/mirte-ros-packages/mirte_telemetrix/config/mirte_user_config.yaml" +sd_config_path = "/mnt/mirte/robot_config.yaml" + + +def start(mount_point, loop): + global observer, sd_config_path + sd_config_path = f"{mount_point}/robot_config.yaml" + if not os.path.isfile(tmx_config_path): + print("No telemetrix configuration, stopping config provisioning") + return + if not os.path.isfile(sd_config_path): + print("No configuration on extra partition, copying existing to it.") + copy(tmx_config_path, sd_config_path) + + # Assuming the sd configuration is the 'latest', as it is either copied from tmx/config or it is updated by the user when offline, so always copy the sd config to the user_config + copy(sd_config_path, tmx_config_path) + observer = Observer() + event_handler = MyEventHandler() + observer.schedule(event_handler, tmx_config_path) + observer.schedule(event_handler, sd_config_path) + observer.start() + + +last_copy = time.time() + + +def copy_on_modify(src_path): + global last_copy + # otherwise it is triggering itself. 1s backoff time + if time.time() - last_copy < 1: + return + last_copy = time.time() + if src_path == tmx_config_path: + copy(src_path, sd_config_path) + else: + copy(src_path, tmx_config_path) + + +class MyEventHandler(FileSystemEventHandler): + def catch_all_handler(self, event): + if event.is_directory: + return + copy_on_modify(event.src_path) + + def on_moved(self, event): + self.catch_all_handler(event) + + def on_created(self, event): + self.catch_all_handler(event) + + def on_deleted(self, event): + self.catch_all_handler(event) + + def on_modified(self, event): + print(event) + self.catch_all_handler(event) + + +async def stop(): + observer.stop() + observer.join() + + +def copy(fr, to): + shutil.copy2(fr, to) + + +# sudo mount /dev/mmcblk0p2 /mnt/mirte -o rw,uid=$(id -u),gid=$(id -g) + +# status = os.stat(tmx_config_path) +# print(status) +# print(os.stat(f"{mount_point}robot_config.yaml")) diff --git a/provisioning/ssh.py b/provisioning/ssh.py new file mode 100644 index 0000000..489dd09 --- /dev/null +++ b/provisioning/ssh.py @@ -0,0 +1,25 @@ +import os + +auth_keys_path = "/home/mirte/.ssh/authorized_keys" + + +def start(mount_point, loop): + config_file = f"{mount_point}/authorized_keys" + if not os.path.isfile(config_file): + print("No authorized keys configuration") + return + existing_keys = [] + with open(config_file, "r") as file: + new_keys = file.readlines() + if os.path.isfile(auth_keys_path): + with open(auth_keys_path) as file: + existing_keys = file.readlines() + + new_keys = list(filter(lambda key: not key in existing_keys, new_keys)) + print("adding:", new_keys) + with open(auth_keys_path, "a") as file: + file.writelines(new_keys) + + +async def stop(): + print("stop ssh") diff --git a/provisioning/store/machine_config.yaml b/provisioning/store/machine_config.yaml new file mode 100644 index 0000000..0e9c951 --- /dev/null +++ b/provisioning/store/machine_config.yaml @@ -0,0 +1,3 @@ +hostname: Mirte-XXXXXX +access_points: [] +password: "" \ No newline at end of file diff --git a/provisioning/store/readme.md b/provisioning/store/readme.md new file mode 100644 index 0000000..be90b58 --- /dev/null +++ b/provisioning/store/readme.md @@ -0,0 +1 @@ +# folder to store file which are done \ No newline at end of file diff --git a/repos.yaml b/repos.yaml index 3dbf8a1..db93b3a 100644 --- a/repos.yaml +++ b/repos.yaml @@ -1,19 +1,19 @@ repositories: mirte-web-interface: type: git - url: https://github.com/mirte-robot/mirte-web-interface.git + url: https://github.com/arendJan/mirte-web-interface.git version: main mirte-telemetrix4arduino: type: git - url: https://github.com/mirte-robot/Telemetrix4Arduino.git - version: master + url: https://github.com/arendJan/Telemetrix4Arduino.git + version: platformio mirte-install-scripts: type: git - url: https://github.com/mirte-robot/mirte-install-scripts.git - version: main + url: https://github.com/arendJan/mirte-install-scripts.git + version: parralel-jobs mirte-telemetrix-aio: type: git - url: https://github.com/mirte-robot/telemetrix-aio.git + url: https://github.com/arendJan/telemetrix-aio.git version: master mirte-tmx-pico-aio: type: git @@ -21,21 +21,29 @@ repositories: version: master mirte-ros-packages: type: git - url: https://github.com/mirte-robot/mirte-ros-packages.git + url: https://github.com/arendJan/mirte-ros-packages.git version: main mirte-oled-images: type: git - url: https://github.com/mirte-robot/mirte-oled-images.git + url: https://github.com/arendJan/mirte-oled-images.git version: main mirte-arduino-libraries: type: git - url: https://github.com/mirte-robot/mirte-arduino-libraries.git + url: https://github.com/arendJan/mirte-arduino-libraries.git version: main mirte-python: type: git - url: https://github.com/mirte-robot/mirte-python.git + url: https://github.com/arendJan/mirte-python.git version: main mirte-documentation: type: git - url: https://github.com/mirte-robot/mirte-documentation.git + url: https://github.com/arendJan/mirte-documentation.git version: main + pico-sdk: + type: git + url: https://github.com/raspberrypi/pico-sdk.git + version: master + mirte-telemetrix4rpipico: + type: git + url: https://github.com/mirte-robot/Telemetrix4RpiPico.git + version: master \ No newline at end of file diff --git a/run_arduino.sh b/run_arduino.sh index 9093a1f..4357361 100755 --- a/run_arduino.sh +++ b/run_arduino.sh @@ -12,32 +12,40 @@ if test "$1" == "upload" && [[ $ROS_RUNNING == "1" ]]; then sudo service mirte-ros stop || /bin/true fi +cd /home/mirte/Arduino/Telemetrix4Arduino || exit # Different build scripts if test "$1" == "build"; then - arduino-cli -v compile --fqbn STM32:stm32:GenF1:pnum=BLUEPILL_F103C8,upload_method=dfu2Method,xserial=generic,usb=CDCgen,xusb=FS,opt=osstd,rtlib=nano /home/mirte/arduino_project/$2 + pio run -e robotdyn_blackpill_f303cc fi if test "$1" == "build_nano"; then - arduino-cli -v compile --fqbn arduino:avr:nano:cpu=atmega328 /home/mirte/arduino_project/$2 + pio run -e nanoatmega328new + # arduino-cli -v compile --fqbn arduino:avr:nano:cpu=atmega328 /home/mirte/arduino_project/$2 fi if test "$1" == "build_nano_old"; then - arduino-cli -v compile --fqbn arduino:avr:nano:cpu=atmega328old /home/mirte/arduino_project/$2 + pio run -e nanoatmega328 + + # arduino-cli -v compile --fqbn arduino:avr:nano:cpu=atmega328old /home/mirte/arduino_project/$2 fi if test "$1" == "build_uno"; then - arduino-cli -v compile --fqbn arduino:avr:uno /home/mirte/arduino_project/$2 + echo "TODO" fi # Different upload scripts if test "$1" == "upload" || test "$1" == "upload_stm32"; then - arduino-cli -v upload -p /dev/ttyACM0 --fqbn STM32:stm32:GenF1:pnum=BLUEPILL_F103C8,upload_method=dfu2Method,xserial=generic,usb=CDCgen,xusb=FS,opt=osstd,rtlib=nano /home/mirte/arduino_project/$2 + pio run -t upload -e robotdyn_blackpill_f303cc + # arduino-cli -v upload -p /dev/ttyACM0 --fqbn STM32:stm32:GenF1:pnum=BLUEPILL_F103C8,upload_method=dfu2Method,xserial=generic,usb=CDCgen,xusb=FS,opt=osstd,rtlib=nano /home/mirte/arduino_project/$2 fi if test "$1" == "upload_nano"; then - arduino-cli -v upload -p /dev/ttyUSB0 --fqbn arduino:avr:nano:cpu=atmega328 /home/mirte/arduino_project/$2 + pio run -t upload -e nanoatmega328new + # arduino-cli -v upload -p /dev/ttyUSB0 --fqbn arduino:avr:nano:cpu=atmega328 /home/mirte/arduino_project/$2 fi if test "$1" == "upload_nano_old"; then - arduino-cli -v upload -p /dev/ttyUSB0 --fqbn arduino:avr:nano:cpu=atmega328old /home/mirte/arduino_project/$2 + pio run -t upload -e nanoatmega328 + # arduino-cli -v upload -p /dev/ttyUSB0 --fqbn arduino:avr:nano:cpu=atmega328old /home/mirte/arduino_project/$2 fi if test "$1" == "upload_uno"; then - arduino-cli -v upload -p /dev/ttyACM0 --fqbn arduino:avr:uno /home/mirte/arduino_project/$2 + pio run -t upload -e nanoatmega328 + # arduino-cli -v upload -p /dev/ttyACM0 --fqbn arduino:avr:uno /home/mirte/arduino_project/$2 fi # Start ROS again diff --git a/services/mirte-usb.service b/services/mirte-usb.service new file mode 100644 index 0000000..226d62c --- /dev/null +++ b/services/mirte-usb.service @@ -0,0 +1,11 @@ +[Unit] +Description=Mirte USB ethernet +After=NetworkManager.service +After=network-online.target + +[Service] +KillMode=process +ExecStart=/bin/bash -c "/usr/local/src/mirte/mirte-install-scripts/usb_ethernet.sh" + +[Install] +WantedBy=multi-user.target diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..51c6cc1 --- /dev/null +++ b/update.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# TODO: Untested and unused for now +MIRTE_SRC_DIR=/usr/local/src/mirte + +export UPDATE=true + +# TODO: update git packages + +{ + # Install telemetrix + cd $MIRTE_SRC_DIR/mirte-telemetrix-aio || exit + pip3 install . + echo "done telemetrix" +} 2>&1 | sed -u 's/^/telemetrix::: /' & + +{ + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit + + . ./install_arduino.sh + echo "done arduino" +} 2>&1 | sed -u 's/^/arduino::: /' & + +if true; then + { + # Install Mirte Python package + cd $MIRTE_SRC_DIR/mirte-python || exit + pip3 install . + echo "done mirte-python" + } 2>&1 | sed -u 's/^/mirte-python::: /' & + + { + # Install Mirte Interface + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit + . ./install_web.sh + echo "done web" + } 2>&1 | sed -u 's/^/web::: /' & + + { + # Install Jupyter Notebook + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit + . ./install_jupyter_ros.sh + echo "done jupyter_ros" + } 2>&1 | sed -u 's/^/jupyter_ros::: /' & + + { + # Install Mirte ROS packages + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit + . ./install_ROS.sh + echo "done ROS" + } 2>&1 | sed -u 's/^/ROS::: /' & + + # Install Mirte documentation + { + . ./install_docs.sh + echo "done docs" + } 2>&1 | sed -u 's/^/docs::: /' & +# Install overlayfs and make sd card read only (software) +# sudo apt install -y overlayroot +# Currently only instaling, not enabled +#sudo bash -c "echo 'overlayroot=\"tmpfs\"' >> /etc/overlayroot.conf" +fi +echo "Waiting" +time wait # wait on all the backgrounded stuff +echo "Done installing"