diff --git a/.github/workflows/c_check.yml b/.github/workflows/c_check.yml new file mode 100644 index 0000000..1927820 --- /dev/null +++ b/.github/workflows/c_check.yml @@ -0,0 +1,36 @@ +name: test-clang-format + +on: [push, pull_request] + +jobs: + c_style_check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: DoozyX/clang-format-lint-action@v0.14 + with: + source: '.' + # exclude: './third_party ./external' + extensions: 'c,h,cpp,hpp' + clangFormatVersion: 14 + style: llvm + c_build_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: DoozyX/clang-format-lint-action@v0.14 + - uses: lukka/get-cmake@latest + - name: Build pam module + run: | + sudo apt update && sudo apt install libpam0g-dev -y + ./install_pam.sh + ./pam/build/myprogram + - name: Build usb_switch + run: | + cd mirte-master/usb_switch/ + sudo apt install libgpiod-dev -y + mkdir build + cd build + cmake .. + make -j diff --git a/.github/workflows/cmake_check.yml b/.github/workflows/cmake_check.yml new file mode 100644 index 0000000..f975b1a --- /dev/null +++ b/.github/workflows/cmake_check.yml @@ -0,0 +1,24 @@ +name: CMake Format Check + +on: + [push, pull_request] + +jobs: + cmake-format-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + + - name: Install cmake-format + run: pip install cmake-format + + - name: Run cmake-format check + run: cmake-format --check **/**/CMakeLists.txt + diff --git a/.github/workflows/repocheck.yml b/.github/workflows/repocheck.yml new file mode 100644 index 0000000..929bd23 --- /dev/null +++ b/.github/workflows/repocheck.yml @@ -0,0 +1,22 @@ +on: + # - push + - pull_request + - workflow_call + +name: "reposcheck" +permissions: {} + +jobs: + reposcheck: + name: reposcheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt update + curl -s https://packagecloud.io/install/repositories/dirk-thomas/vcstool/script.deb.sh | sudo bash + sudo apt-get update + sudo apt-get install python3-vcstool -y + mkdir check + - name: check + run: vcs import --input repos.yaml check diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index c6be2b3..eda7a85 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -10,8 +10,11 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run ShellCheck uses: ludeeus/action-shellcheck@master with: severity: warning + - name: Check executable bit sh files + run: | + find . -type f -name "*.sh" -exec sh -c 'if ! test -x {}; then echo "File {} is not executable"; exit 1; fi' \; diff --git a/.github/workflows/shfmt.yml b/.github/workflows/shfmt.yml index 59ca713..3f0ca5c 100644 --- a/.github/workflows/shfmt.yml +++ b/.github/workflows/shfmt.yml @@ -6,7 +6,7 @@ jobs: sh-checker: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run the sh-checker uses: luizm/action-sh-checker@master env: diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml new file mode 100644 index 0000000..c03eeea --- /dev/null +++ b/.github/workflows/test_install.yml @@ -0,0 +1,106 @@ +name: test-install-parts + +on: [push, pull_request] + +jobs: + arduino-install: + runs-on: ubuntu-22.04 + strategy: + matrix: + install_arduino_all: [true, false] + fail-fast: false + + steps: + - uses: actions/checkout@v4 + - shell: bash + run: | + mkdir -p /etc/udev/rules.d/ # just for testing, folder should just exist on the real image + sudo useradd -m -s /bin/bash mirte + pwd + export MIRTE_SRC_DIR=~/usr/local/src/mirte + mkdir -p $MIRTE_SRC_DIR + + echo "Matrix install_arduino_all=${{ matrix.install_arduino_all }}" + if [ "${{ matrix.install_arduino_all }}" = "true" ]; then + echo "Setting INSTALL_ARDUINO_ALL to true" + echo "export INSTALL_ARDUINO_ALL=true" > $MIRTE_SRC_DIR/settings.sh + else + echo "Setting INSTALL_ARDUINO_ALL to false" + echo "export INSTALL_ARDUINO_ALL=false" > $MIRTE_SRC_DIR/settings.sh + fi + echo "Contents of settings.sh:" + cat $MIRTE_SRC_DIR/settings.sh || echo "settings.sh does not exist yet" + + ls -ld /usr/local/ + ls -ld /usr/local/src/ + sudo mkdir -p $MIRTE_SRC_DIR + sudo mkdir $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo cp -r ./* $MIRTE_SRC_DIR/mirte-install-scripts/ + cd $MIRTE_SRC_DIR + sudo apt update + sudo git clone https://github.com/mirte-robot/telemetrix4arduino.git mirte-telemetrix4arduino -b develop + sudo git clone https://github.com/mirte-robot/telemetrix4rpipico.git mirte-telemetrix4rpipico -b develop --recursive + cd $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -E ./install_arduino.sh + + ros2-install: + runs-on: ubuntu-22.04 + container: ghcr.io/catthehacker/ubuntu:act-22.04 + strategy: + matrix: + type: [mirte, mirte-master] + fail-fast: false + steps: + - uses: actions/checkout@v4 + - shell: bash + run: | + echo $0 + sudo useradd -m -s /bin/bash mirte + # allow nopasswd sudo for mirte user + echo "mirte ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/mirte + sudo chmod 777 /home/runner/ || true + export MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} + sudo mkdir -p $MIRTE_SRC_DIR + sudo chown mirte: $MIRTE_SRC_DIR + sudo chmod 755 $MIRTE_SRC_DIR + sudo -u mirte mkdir $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -u mirte cp -r ./* $MIRTE_SRC_DIR/mirte-install-scripts/ + cd $MIRTE_SRC_DIR + sudo -u mirte git clone https://github.com/arendjan/mirte-ros-packages.git mirte-ros-packages -b develop --recursive + + sudo apt update + cd $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -u mirte sed -i '/systemctl/d' install_ROS2.sh # remove systemctl command as it is not available in the github runner/container + + # TODO: add deb proxy if wanted + # sudo apt install dbus libunwind-dev squid-deb-proxy-client -y + # mkdir -p /run/dbus + # dbus-daemon --system + # # sed -i 's/.*enable-dbus=.*/enable-dbus=no/' /etc/avahi/avahi-daemon.conf + # avahi-daemon -D & + MIRTE_TYPE=${{ matrix.type }} + sudo -u mirte /bin/bash -c "MIRTE_TYPE=${{ matrix.type }} source ./install_ROS2.sh" + + web-install: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - shell: bash + run: | + sudo useradd -m -s /bin/bash mirte + # allow root from user + echo "mirte ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/mirte + export MIRTE_SRC_DIR=/test/mirte + sudo mkdir -p $MIRTE_SRC_DIR + sudo chown mirte: $MIRTE_SRC_DIR + sudo chmod 755 $MIRTE_SRC_DIR + sudo -u mirte mkdir $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -u mirte cp -r ./* $MIRTE_SRC_DIR/mirte-install-scripts/ + cd $MIRTE_SRC_DIR + sudo -u mirte git clone https://github.com/mirte-robot/mirte-web-interface.git mirte-web-interface -b develop + # remove all lines with systemctl as not available in github runner + cd $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -u mirte sed -i '/sudo systemctl/d' install_web.sh + cd $MIRTE_SRC_DIR/mirte-install-scripts/ + sudo -u mirte /bin/bash -c "MIRTE_SRC_DIR=$MIRTE_SRC_DIR ./install_web.sh" + sudo -u mirte /bin/bash -c "MIRTE_SRC_DIR=$MIRTE_SRC_DIR ./install_vscode.sh" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53fcc16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +ui/ +*.uf2 +ev3-usb.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6a4e589 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: local + hooks: + - id: clang-format + name: clang-format + entry: bash -c 'FILES=`find -type f -regex ".*\.\(cpp\|hpp\|h\|c\)$" -not -path "*/build/*"`; clang-format-14 --Werror $FILES -style=llvm -i' + language: system + types: [c] + pass_filenames: false + - id: cmake-format + entry: bash -c 'FILES=`find . -name "CMakeLists.txt"`; cmake-format $FILES -i' + language: system + types: [cmake] + pass_filenames: false + name: cmake-format + - id: shell-format + entry: bash -c 'shopt -s globstar; /home/arendjan/go/bin/shfmt -s -w **/**.sh' + language: system + types: [shell] + pass_filenames: false + name: shell-format diff --git a/LICENSE b/LICENSE index fc45ba6..dee5c37 100644 --- a/LICENSE +++ b/LICENSE @@ -1,28 +1,201 @@ -BSD 3-Clause License - -Copyright (c) 2024, Martin Klomp, MIRTE-team - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025, MIRTE team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..0090a31 --- /dev/null +++ b/NOTICE @@ -0,0 +1,7 @@ +This software is licensed under the Apache License, Version 2.0. +See the LICENSE file for details. + +Technische Universiteit Delft hereby disclaims all copyright interest +in the software of "MIRTE robot" (an educational robot) written by the +Author(s). +prof. dr. ir. Fred van Keulen, Dean of Mechanical Engineering diff --git a/README.md b/README.md index a7c1e50..2737204 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,42 @@ -# Mirte Install Scripts -Scripts that are ran during creation of the image and on the Mirte itself. +# mirte-install-scripts + +These scripts will install all the software needed for the MIRTE OS. This +can be run when creating an image, or on the robot itself. + +## Install + +You can install this by running the main script: + +``` +./install_mirte.sh +``` + +## Checks + +To contribute to this repository the code needs to pass the shell +[style checks](). -## Stylecheck - [shfmt](https://github.com/mvdan/sh): styling (whitespace) of all shell scripts -- [ShellCheck](https://github.com/koalaman/shellcheck): static analysis to find (possible) bugs. Only errors and warnings are enabled -Check & format using their command line tools or vscode plugins. Or check the GitHub workflows with (act)[https://github.com/nektos/act]. \ No newline at end of file + + To check this locally before you commit/push: + ```sh + curl -sS https://webi.sh/shfmt | sh + find . -type f -name "*.sh" -print0 | xargs -0 shfmt -d + # Fix by using: + find . -type f -name "*.sh" -print0 | xargs -0 shfmt -w + ``` + +- [ShellCheck](https://github.com/koalaman/shellcheck): static analysis to find + (possible) bugs. Only errors and warnings are enabled. + + To check this locally before you commit/push: + ```sh + curl -sS https://webi.sh/shellcheck | sh + find . -type f -name "*.sh" -print0 | xargs -0 shellcheck --severity=warning + # Fix manually + ``` + +## License + +This work is licensed under a Apache-2.0 OSS license. diff --git a/blink.sh b/blink.sh index 7594ed1..a440b19 100755 --- a/blink.sh +++ b/blink.sh @@ -2,58 +2,57 @@ BLINK_SPEED=.5 VALUE=$1 -OPI=$(uname -a | grep sunxi) -OPI2=$(grep "Orange Pi Zero 2" /proc/device-tree/model) -RPI=$(grep -a "Raspberry" /proc/device-tree/model) -if [ "$RPI" ]; then - echo none >/sys/class/leds/ACT/trigger - echo none >/sys/class/leds/PWR/trigger -fi +findleds() { + LEDS_ALL=$(ls /sys/class/leds) + # try to set green as first led, red as second + # if not found, set the first two leds found + if echo "$LEDS_ALL" | grep -q "green"; then + GR_LED="$(echo "$LEDS_ALL" | grep "green")" + elif echo "$LEDS_ALL" | grep -q "ACT"; then + GR_LED="$(echo "$LEDS_ALL" | grep "ACT")" + else + GR_LED="$(echo "$LEDS_ALL" | head -n 1)" + fi -red_on() { - if [ "$OPI" ]; then - echo 'default-on' >/sys/class/leds/orangepi\:red\:status/trigger - elif [ "$OPI2" ]; then - echo '255' >/sys/class/leds/orangepi\:red\:power/brightness - elif [ "$RPI" ]; then - echo 1 >/sys/class/leds/PWR/brightness + if echo "$LEDS_ALL" | grep -q "red"; then + RED_LED="$(echo "$LEDS_ALL" | grep "red")" + elif echo "$LEDS_ALL" | grep -q "PWR"; then + RED_LED="$(echo "$LEDS_ALL" | grep "PWR")" + else + RED_LED="$(echo "$LEDS_ALL" | head -n 2 | tail -n 1)" fi + + echo "RED: $RED_LED" + echo "GREEN: $GR_LED" +} +findleds +# store trigger to reset later to, take only part in [ ] to remove the list of options +# GR_TRIGGER="$(cat /sys/class/leds/$GR_LED/trigger | awk -F'[][]' '{print $2}')" +# RED_TRIGGER="$(cat /sys/class/leds/$RED_LED/trigger | awk -F'[][]' '{print $2}')" + +echo "none" >/sys/class/leds/$GR_LED/trigger +echo "none" >/sys/class/leds/$RED_LED/trigger + +red_on() { + echo 'default-on' >/sys/class/leds/$RED_LED/trigger } red_off() { - if [ "$OPI" ]; then - echo 'none' >/sys/class/leds/orangepi\:red\:status/trigger - elif [ "$OPI2" ]; then - echo '0' >/sys/class/leds/orangepi\:red\:power/brightness - elif [ "$RPI" ]; then - echo '0' >/sys/class/leds/PWR/brightness - fi + echo 'none' >/sys/class/leds/$RED_LED/trigger } green_on() { - if [ "$OPI" ]; then - echo 'default-on' >/sys/class/leds/orangepi\:green\:pwr/trigger - elif [ "$OPI2" ]; then - echo '255' >/sys/class/leds/orangepi\:green\:status/brightness - elif [ "$RPI" ]; then - echo 1 >/sys/class/leds/ACT/brightness - fi + echo 'default-on' >/sys/class/leds/$GR_LED/trigger } green_off() { - if [ "$OPI" ]; then - echo 'none' >/sys/class/leds/orangepi\:green\:pwr/trigger - elif [ "$OPI2" ]; then - echo '0' >/sys/class/leds/orangepi\:green\:status/brightness - elif [ "$RPI" ]; then - echo 0 >/sys/class/leds/ACT/brightness - fi + echo 'none' >/sys/class/leds/$GR_LED/trigger } echo "Blinking" echo "$VALUE" -for ((repeat = 0; repeat < 5; repeat++)); do +while true; do # Start sequence with fast blinking both for ((i = 0; i < 10; i++)); do FAST=$(echo "scale=2; $BLINK_SPEED/20" | bc) @@ -100,6 +99,6 @@ for ((repeat = 0; repeat < 5; repeat++)); do done done -# Reset to defaults -green_on -red_off +# Set to nice blinking, defaults are often heartbeat and off. +echo "activity" >/sys/class/leds/$GR_LED/trigger +echo "heartbeat" >/sys/class/leds/$RED_LED/trigger diff --git a/check_pico.sh b/check_pico.sh new file mode 100755 index 0000000..4329ecd --- /dev/null +++ b/check_pico.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +sudo picotool verify -f ../mirte-telemetrix4rpipico/build/Telemetrix4RpiPico.uf2 +if [ $? -eq 0 ]; then + echo "Verification successful!" +else + echo "Verification failed!" + echo "running upload" + sudo picotool load -f ../mirte-telemetrix4rpipico/build/telemetrix4rpipico.uf2 +fi + +sleep 5 +# if lsusb has pico boot, then run reboot +if lsusb | grep -q "Raspberry Pi RP2 Boot"; then + echo "Pico found, rebooting..." + sudo picotool reboot +fi diff --git a/config/code_server_config.yaml b/config/code_server_config.yaml new file mode 100644 index 0000000..144949f --- /dev/null +++ b/config/code_server_config.yaml @@ -0,0 +1,10 @@ +# Don't have network, so don't try to upload or download +disable-update-check: true +disable-telemetry: true +disable-workspace-trust: true # Trust everything on the Mirte robot +bind-addr: 127.0.0.1:9000 # Nginx will forward it from this location +auth: none # password authentication is done by nginx as local is allowed +# password: _PASSWORD_ # no need for password +cert: false +disable-getting-started-override: true # don't show coder advertisement +app-name: "Mirte" # does not work ATM \ No newline at end of file diff --git a/create_user.sh b/create_user.sh index 5686194..018ef63 100755 --- a/create_user.sh +++ b/create_user.sh @@ -1,14 +1,18 @@ #!/bin/bash set -ex -MIRTE_SRC_DIR=/usr/local/src/mirte - +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} # Add mirte user with sudo rights #TODO: user without homedir (create homedir for user) sudo useradd -m -G sudo,audio -s /bin/bash mirte sudo mkdir /home/mirte/workdir sudo chown mirte:mirte /home/mirte/workdir -echo -e "mirte_mirte\nmirte_mirte" | sudo passwd mirte + +sudo ./install_pam.sh # setup pam before changing the password for the mirte user. + +echo "mirte:mirte_mirte" | sudo chpasswd +echo "root:mirte_mirte" | sudo chpasswd + sudo mkdir -p $MIRTE_SRC_DIR sudo chown mirte:mirte $MIRTE_SRC_DIR diff --git a/download_repos.sh b/download_repos.sh index 1f9139e..2bcbc57 100755 --- a/download_repos.sh +++ b/download_repos.sh @@ -1,12 +1,20 @@ #!/bin/bash set -xe # Install vcstool -sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /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-get update +ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') +curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" +sudo apt install /tmp/ros2-apt-source.deb +sudo apt update sudo apt-get install -y python3-vcstool # Download all Mirte repositories vcs import --workers 1 serial="10" # original does not work anymore for newer kernels, needs 'random' value +59,66d58 +< +< +< tmpdir=$(mktemp -d) +< mount /dev/mmcblk0p1 $tmpdir +< if [ -f $tmpdir/ev3dev.txt ]; then +< cdc_only=$(grep -i "^cdc_only=\(true\|yes\|1\)" $tmpdir/ev3dev.txt | cut -b 10-) +< fi +< umount $tmpdir diff --git a/fix_scripts/readme.md b/fix_scripts/readme.md new file mode 100644 index 0000000..97c2141 --- /dev/null +++ b/fix_scripts/readme.md @@ -0,0 +1,3 @@ +# Scripts for online updates of Mirte-master +#TODO: fix for ROS2 if required +Shouldn't be required anymore, but can be used to check for updates and calibrate arm. \ No newline at end of file diff --git a/install_ROS.sh b/install_ROS.sh index f7cddbb..9d15268 100755 --- a/install_ROS.sh +++ b/install_ROS.sh @@ -1,7 +1,9 @@ #!/bin/bash set -xe + +THIS SCRIPT IS NOT USED ANYMORE, LOOK AT install_ROS2.sh #TODO: get this as a parameter -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} # There is a bug with Cmake in qemu for armhf: # https://gitlab.kitware.com/cmake/cmake/-/issues/20568 @@ -11,14 +13,15 @@ wget https://apt.kitware.com/kitware-archive.sh chmod +x kitware-archive.sh sudo ./kitware-archive.sh rm kitware-archive.sh -sudo apt update +sudo apt update || true sudo apt install cmake -y # Install ROS Noetic sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /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 +sudo apt update || true 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 +grep -qxF "source /opt/ros/noetic/setup.zsh" /home/mirte/.zshrc || echo "source /opt/ros/noetic/setup.zsh" >>/home/mirte/.zshrc source /opt/ros/noetic/setup.bash sudo rosdep init rosdep update @@ -38,9 +41,42 @@ mkdir -p /home/mirte/mirte_ws/src cd /home/mirte/mirte_ws/src || exit 1 ln -s $MIRTE_SRC_DIR/mirte-ros-packages . cd .. -rosdep install -y --from-paths src/ --ignore-src --rosdistro noetic + +if [[ $MIRTE_TYPE == "mirte-master" ]]; then + # install lidar and depth camera + cd /home/mirte/mirte_ws/src || exit 1 + git clone https://github.com/Slamtec/rplidar_ros.git + git clone https://github.com/arendjan/ros_astra_camera.git -b fix-image-transport # compressed images + git clone https://github.com/arendjan/ridgeback.git + cd ../../ + mkdir temp + cd temp || exit 1 + sudo apt install -y libudev-dev + git clone https://github.com/libuvc/libuvc.git + cd libuvc + mkdir build && cd build + cmake .. && make -j4 + sudo make install + sudo ldconfig + cd ../../../ + sudo rm -rf temp + cd /home/mirte/mirte_ws/ || exit 1 + rosdep install -y --from-paths src/ --ignore-src --rosdistro noetic + catkin build + source ./devel/setup.bash + roscd astra_camera + ./scripts/create_udev_rules + sudo udevadm control --reload && sudo udevadm trigger + roscd rplidar_ros + ./scripts/create_udev_rules.sh +fi + +# -r is for continue on error, mirte-ros-packages references astra_camera which is not installed on default images. +rosdep install -y --from-paths src/ --ignore-src --rosdistro noetic -r catkin build grep -qxF "source /home/mirte/mirte_ws/devel/setup.bash" /home/mirte/.bashrc || echo "source /home/mirte/mirte_ws/devel/setup.bash" >>/home/mirte/.bashrc +grep -qxF "source /home/mirte/mirte_ws/devel/setup.zsh" /home/mirte/.zshrc || echo "source /home/mirte/mirte_ws/devel/setup.zsh" >>/home/mirte/.zshrc + source /home/mirte/mirte_ws/devel/setup.bash # install missing python dependencies rosbridge @@ -48,13 +84,17 @@ source /home/mirte/mirte_ws/devel/setup.bash #sudo pip3 install twisted pyOpenSSL autobahn tornado pymongo # Add systemd service to start ROS nodes -sudo rm /lib/systemd/system/mirte-ros.service || true -sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ros.service /lib/systemd/system/ +ROS_SERVICE_NAME=mirte-ros +if [[ $MIRTE_TYPE == "mirte-master" ]]; then # master version should start a different launch file + ROS_SERVICE_NAME=mirte-master-ros +fi +sudo rm /lib/systemd/system/$ROS_SERVICE_NAME.service || true +sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/$ROS_SERVICE_NAME.service /lib/systemd/system/ sudo systemctl daemon-reload -sudo systemctl stop mirte-ros || /bin/true -sudo systemctl start mirte-ros -sudo systemctl enable mirte-ros +sudo systemctl stop $ROS_SERVICE_NAME || /bin/true +sudo systemctl start $ROS_SERVICE_NAME +sudo systemctl enable $ROS_SERVICE_NAME sudo usermod -a -G video mirte diff --git a/install_ROS2.sh b/install_ROS2.sh new file mode 100755 index 0000000..103cc4f --- /dev/null +++ b/install_ROS2.sh @@ -0,0 +1,248 @@ +#!/bin/bash +set -xe +# IMPORTANT: +# Do not upgrade apt-get since it will break the image. libc-bin will for some +# reason break and not be able to install new stuff on the image. +# TODO: check above info, no issues yet (2024-12-11) + +#TODO: get this as a parameter +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} +. tools.sh +# shellcheck source=/dev/null +source /etc/os-release + +# Install ROS 2 (Humble) +sudo apt install software-properties-common -y +sudo add-apt-repository universe -y +sudo apt update && sudo apt install curl -y +ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') +if [ -z "$ROS_APT_SOURCE_VERSION" ]; then + ROS_APT_SOURCE_VERSION="1.1.0" +fi +curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb" +sudo apt install /tmp/ros2-apt-source.deb +sudo apt update + +# select ros2 type based on UBUNTU_CODENAME +if [[ $UBUNTU_CODENAME == "jammy" ]]; then + ROS_NAME=humble +elif [[ $UBUNTU_CODENAME == "noble" ]]; then + ROS_NAME=jazzy +else + echo "Unknown UBUNTU_CODENAME: $UBUNTU_CODENAME" + exit 1 +fi + +sudo apt install -y ros-$ROS_NAME-ros-base ros-$ROS_NAME-zenoh-bridge-dds ros-$ROS_NAME-rmw-zenoh-cpp +sudo apt install -y ros-$ROS_NAME-xacro +sudo apt install -y ros-dev-tools ccache + +# shellcheck source=/dev/null +source /opt/ros/$ROS_NAME/setup.bash +sudo rosdep init +rosdep update + +# Install computer vision libraries +#TODO: make dependecies of ROS package +sudo apt install -y python3-pip python3-wheel python3-setuptools python3-opencv libzbar0 +sudo pip3 install pyzbar mergedeep + +# TODO: move configs to mirte bringup +#cp $MIRTE_SRC_DIR/mirte-ros-packages/mirte_telemetrix/config/mirte_user_settings.yaml /home/mirte/.user_settings.yaml +#rm $MIRTE_SRC_DIR/mirte-ros-packages/mirte_telemetrix/config/mirte_user_settings.yaml +#ln -s /home/mirte/.user_settings.yaml $MIRTE_SRC_DIR/mirte-ros-packages/config/mirte_user_settings.yaml + +# TODO: install in a separate workspace or install the debs. +# Install Mirte ROS package +mkdir -p /home/mirte/mirte_ws/src +cd /home/mirte/mirte_ws/src +ln -s $MIRTE_SRC_DIR/mirte-ros-packages . + +sudo apt install python3-colcon-mixin -y +colcon mixin add default https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml +colcon mixin update default + +# Add colcon_defaults.yaml to the workspace, symlink and ccache +cat <>/home/mirte/mirte_ws/colcon_defaults.yaml +# Default colcon build options for the Mirte workspace +build: + symlink-install: true + mixin: + - "ccache" +EOF + +# if mirte-ros-packages is from main or develop, use the precompiled version, otherwise compile on-device +cd $MIRTE_SRC_DIR/mirte-ros-packages +git submodule update --init --recursive +branch=$(git rev-parse --abbrev-ref HEAD) +arch=$(dpkg --print-architecture) +ubuntu_version=$(lsb_release -cs) +github_url=$(git config --get remote.origin.url | sed 's/\.git$//') +fallback=true +cd /home/mirte/mirte_ws/ || exit 1 +vcs import src <$MIRTE_SRC_DIR/mirte-ros-packages/sources.repos || true +if [[ $branch == "develop" || $branch == "main" ]]; then + fallback=false + + # Install mirte ros packages with apt from github, since they take ages to compile and it's easier to update them. + # colcon ignore those packages + if [[ $branch == "develop" ]]; then + arch="${arch}_develop" + fi + echo "Using precompiled version of packages" + echo "deb [trusted=yes] $github_url/raw/ros_mirte_${ROS_NAME}_${ubuntu_version}_${arch}/ ./" | sudo tee /etc/apt/sources.list.d/mirte-ros-packages.list + echo "yaml $github_url/raw/ros_mirte_${ROS_NAME}_${ubuntu_version}_${arch}/local.yaml ${ROS_NAME}" | sudo tee /etc/ros/rosdep/sources.list.d/mirte-ros-packages.list + sudo apt update + + cd /home/mirte/mirte_ws/src/ || exit 1 + ignore=(mirte_telemetrix_cpp mirte_msgs mirte_teleop astra_camera astra_camera_msgs libuvc mirte_base_control mirte_master_arm_control mirte_control usb_cam) + packages='' + for i in "${ignore[@]}"; do + path=$(colcon list --packages-select $i -p) + # if path is empty, skip + if [[ -z $path ]]; then + echo "Package $i not found, skipping." + continue + fi + path=$(realpath "$path") + echo "Checking package $i at $path" + if [[ ! -d $path ]]; then + echo "Package $i not found, skipping." + continue + fi + touch $path/COLCON_IGNORE + i_dash=$(echo $i | tr '_' '-') + pkg_name="ros-$ROS_NAME-$i_dash" + # check if dbgsym package exists + sudo apt-cache madison "$pkg_name-dbgsym" + + if sudo apt-cache madison "$pkg_name-dbgsym" | grep -vqz "Unable to locate package"; then + echo "Adding debug package $pkg_name-dbgsym" + packages="$packages $pkg_name-dbgsym" + fi + packages="$packages $pkg_name" + done + sudo apt install -y -m $packages || fallback=false # TODO: disabled fallback for now as mirte-arm doesn't compile. +fi + +if $fallback; then + echo "Compiling mirte-ros-packages on-device" + cd /home/mirte/mirte_ws/src/mirte-ros-packages || exit 1 + find . -name "COLCON_IGNORE" -type f -delete +fi + +sudo apt install libboost-all-dev -y +cd /home/mirte/mirte_ws/src || exit 1 +# git clone https://github.com/AlexKaravaev/ros2_laser_scan_matcher +# git clone https://github.com/AlexKaravaev/csm +# git clone https://github.com/ldrobotSensorTeam/ldlidar_stl_ros2 +# git clone https://github.com/RobotWebTools/web_video_server.git -b ros2 +cd .. || exit 1 +rosdep install -y --from-paths src/ --ignore-src --rosdistro $ROS_NAME +colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release +add_mirte_settings "export MIRTE_ZENOH=false" # disable zenoh by default, but can be enabled by setting to true +add_mirte_settings "export MIRTE_USE_MULTIROBOT=false" # TODO: use this in the launch file? +add_mirte_settings "export ROS_LOG_DIR=/tmp/ros" +add_rc "source /home/mirte/.mirte_settings.sh" +add_rc "# Enable Zenoh and multirobot in .mirte_settings.sh" + +add_rc "source /home/mirte/mirte_ws/install/setup.bash" "# sourced later on" + +# By default the ROS communication should only be localhost. +add_mirte_settings "export ROS_LOCALHOST_ONLY=1" + +# shellcheck source=/dev/null +source /home/mirte/mirte_ws/install/setup.bash + +# Add systemd service to start ROS nodes +if [[ $MIRTE_TYPE == "mirte-master" ]]; then # master version should start a different launch file + add_mirte_settings "export ROS_LOCALHOST_ONLY=0" + # rename the service file to the correct name, otherwise systemctl will error with a "Failed to look up unit file state: Link has been severed" error + mv $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ros.service $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ros-pioneer.service + mv $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-master-ros.service $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ros.service +fi +sudo rm /lib/systemd/system/mirte-ros.service || true +# uses same service name, but different links. The service file starts mirte_ros with the correct launch file as argument +sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-ros.service /lib/systemd/system/mirte-ros.service +sudo systemctl daemon-reload +sudo systemctl stop mirte-ros || /bin/true +sudo systemctl start mirte-ros +sudo systemctl enable mirte-ros + +sudo usermod -a -G video mirte +sudo adduser mirte dialout + +# Some nice extra packages: clean can clean workspaces and packages. No need to do it by hand. lint can check for errors in the cmake/package code. +sudo pip3 install colcon-clean colcon-lint + +if [[ $MIRTE_TYPE == "mirte-master" ]]; then + # TODO: need to check and edit the next part: + sudo apt install ros-$ROS_NAME-slam-toolbox -y + + # install lidar and depth camera + cd /home/mirte/mirte_ws/src || exit 1 + git clone https://github.com/ArendJan/rplidar_ros.git --single-branch -b auto_standby_launch_param_c1 # FIXME-FUTURE: When merged, switch back to official repo + + # git clone https://github.com/ArendJan/ros2_astra_camera.git -b fix-ros-jammy # compressed images image transport fixes, fork of orbbec/... with also lazy nodes + # git clone https://github.com/clearpathrobotics/clearpath_mecanum_drive_controller # FIXME: Can be installed from apt? why build? + # cd ../../ + # mkdir temp + # cd temp || exit 1 + # sudo apt install -y libudev-dev libusb-1.0-0-dev nlohmann-json3-dev + # # Install lubuvc-dev manually for newer version + # git clone https://github.com/libuvc/libuvc.git + # cd libuvc + # mkdir build && cd build + # cmake .. && make -j4 + # sudo make install + # sudo ldconfig + # cd ../../../ + # sudo rm -rf temp + cd /home/mirte/mirte_ws/ || exit 1 + rosdep install -y --from-paths src/ --ignore-src --rosdistro $ROS_NAME + colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release + # shellcheck source=/dev/null + source ./install/setup.bash + cd src/ros2_astra_camera/astra_camera + chmod +x ./scripts/install.sh || true + sudo ./scripts/install.sh || true + sudo udevadm control --reload && sudo udevadm trigger + cd ../../rplidar_ros + chmod +x ./scripts/create_udev_rules.sh || true + ./scripts/create_udev_rules.sh || true + +fi + +# Add colcon top level workspace, this makes it possible to run colcon build from any folder, it will find the workspace and build it. Otherwise it will create a new workspace in the subdirectory. +pip install git+https://github.com/arendjan/colcon-top-level-workspace.git + +# zsh does not work nicely with ros2 autocomplete, so we need to add a function to fix it. +# ROS 2 Foxy should have this fixed, but we are using ROS 2 Humble. +# TODO: check for ROS2 jazzy +cat <>/home/mirte/.zshrc +sr () { # macro to source the workspace and enable autocompletion. sr stands for source ros, no other command should use this abbreviation. + . /opt/ros/humble/setup.zsh + . ~/mirte_ws/install/setup.zsh + eval "\$(register-python-argcomplete3 ros2)" + eval "\$(register-python-argcomplete3 colcon)" +} +cb () { + pkg=\$1 + # if package not empty + if [ -n "\$pkg" ]; then + colcon build --symlink-install --packages-up-to \$pkg + else + colcon build --symlink-install + fi +} +cbr () { + pkg=\$1 + # if package not empty + if [ -n "\$pkg" ]; then + colcon build --symlink-install --packages-up-to \$pkg --cmake-args -DCMAKE_BUILD_TYPE=Release + else + colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release + fi +} +sr +EOF diff --git a/install_arduino.sh b/install_arduino.sh index 9c57a2b..7bea4c2 100755 --- a/install_arduino.sh +++ b/install_arduino.sh @@ -1,89 +1,101 @@ #!/bin/bash set -xe -MIRTE_SRC_DIR=/usr/local/src/mirte - -# NOTE: on some builds cmake is very old, so we need to install a newer version, this is done by ./install_ROS.sh, which must be run before this script - +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} +export INSTALL_ARDUINO_ALL=false +. $MIRTE_SRC_DIR/settings.sh +. $MIRTE_SRC_DIR/mirte-install-scripts/tools.sh # 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 - +# install platformio +curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py -o get-platformio.py +python3 get-platformio.py +rm get-platformio.py + +# Add platformio to path +export PATH=$PATH:$HOME/.local/bin +mkdir -p ~/.local/bin || true +ln -s ~/.platformio/penv/bin/platformio ~/.local/bin/platformio +ln -s ~/.platformio/penv/bin/pio ~/.local/bin/pio +ln -s ~/.platformio/penv/bin/piodebuggdb ~/.local/bin/piodebuggdb +pio --version + +add_rc 'export PATH=$PATH:$HOME/.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 + +if [ "$INSTALL_ARDUINO_ALL" = "true" ]; then + # Install picotool for the Raspberry Pi Pico + sudo apt install gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib build-essential pkg-config libusb-1.0-0-dev cmake -y + + # Remove newlib versions that are not compatible with the pico or pico2, otherwise it takes 2GB of space + cd /usr/lib/arm-none-eabi/newlib/thumb || true + sudo rm -rf v8-a* || true + sudo rm -rf v7* || true + + cd $MIRTE_SRC_DIR || exit 1 + mkdir pico/ + cd pico/ || exit 1 + git clone https://github.com/raspberrypi/pico-sdk.git --single-branch --recursive --depth=1 # somehow needed for picotool + ls + realpath pico-sdk + ls + export PICO_SDK_PATH=$MIRTE_SRC_DIR/pico/pico-sdk + add_rc "export PICO_SDK_PATH=$MIRTE_SRC_DIR/pico/pico-sdk" + + cd $MIRTE_SRC_DIR/mirte-telemetrix4rpipico || exit 1 + git submodule update --init --recursive + +else + echo "Skipping installation of Pico tools" + echo "Only installing tools to upload to Pico with default uf2" + # download latest picotool for current arch linux + arch=$(uname -m) + curl -s https://api.github.com/repos/raspberrypi/pico-sdk-tools/releases/latest | grep -F "browser_download_url" | awk -F\" '{print $4}' | grep "picotool-.*-$arch-lin.tar.gz" | wget -i - -O /tmp/picotool-latest-$arch-lin.tar.gz + # Check that the file was downloaded and is a valid tar.gz + if [ ! -s /tmp/picotool-latest-$arch-lin.tar.gz ]; then + echo "Error: Failed to download picotool tarball for architecture $arch." + exit 1 + fi + if ! gzip -t /tmp/picotool-latest-$arch-lin.tar.gz 2>/dev/null; then + echo "Error: Downloaded picotool tarball is not a valid gzip file." + exit 1 + fi + # unzip only picotool/picotool file to /usr/local/bin/picotool + cd /usr/local/bin || exit 1 + sudo tar -xzvf /tmp/picotool-latest-$arch-lin.tar.gz picotool/picotool --strip-components=1 + sudo chmod +x ./picotool + + # uploader when using uart (mirte pioneer pcb) + pip install -U "pip>=25" || true # pico-py-serial-flash requires a newer version of pip, otherwise it'll be installed as UNKNOWN package + pip install git+https://github.com/arendjan/pico-py-serial-flash.git@cli # uart flashing utility when using the pcb + + # download last uf2 from telemetrix pico repo + cd $MIRTE_SRC_DIR/mirte-telemetrix4rpipico || exit 1 + mkdir -p build || true + cd build || exit 1 + REPO=$(git config --get remote.origin.url | sed 's/https:\/\/github.com\///' | sed 's/\.git//') + BRANCH=$(git rev-parse --abbrev-ref HEAD) + curl -s https://api.github.com/repos/$REPO/releases | jq "[.[] | select ((.target_commitish==\"$BRANCH\"))][0]" | grep -F "browser_download_url" | awk -F\" '{print $4}' | grep '\.uf2$' | wget -i - -O Telemetrix4RpiPico.uf2 + #also download elf file + curl -s https://api.github.com/repos/$REPO/releases | jq "[.[] | select ((.target_commitish==\"$BRANCH\"))][0]" | grep -F "browser_download_url" | awk -F\" '{print $4}' | grep '\.elf$' | wget -i - -O Telemetrix4RpiPico.elf + # if not found, try to get from main branch + if [ ! -f Telemetrix4RpiPico.uf2 ]; then + curl -s https://api.github.com/repos/$REPO/releases | jq "[.[] | select ((.target_commitish==\"main\"))][0]" | grep -F "browser_download_url" | awk -F\" '{print $4}' | grep '\.uf2$' | wget -i - -O Telemetrix4RpiPico.uf2 + curl -s https://api.github.com/repos/$REPO/releases | jq "[.[] | select ((.target_commitish==\"main\"))][0]" | grep -F "browser_download_url" | awk -F\" '{print $4}' | grep '\.elf$' | wget -i - -O Telemetrix4RpiPico.elf + echo "Downloaded Telemetrix4RpiPico.uf2 from main branch" + fi + if [ ! -f Telemetrix4RpiPico.uf2 ]; then + echo "Failed to download Telemetrix4RpiPico.uf2" + exit 1 + fi +fi +cd $MIRTE_SRC_DIR/mirte-install-scripts/ +# Already build all versions so only upload is needed *don't do for all, as it requires loads of space for the tools. +# ./run_arduino.sh build Telemetrix4Arduino +# ./run_arduino.sh upload_nano Telemetrix4Arduino # 'try to upload to the nano', to also install the upload tools. +# ./run_arduino.sh build_nano_old Telemetrix4Arduino +./run_arduino.sh build_pico +pio system prune -f # 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 uploading 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' - -# Install picotool for the Raspberry Pi Pico -sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake -y -cd /tmp/ || exit 1 -git clone https://github.com/raspberrypi/pico-sdk.git # somehow needed for picotool -export PICO_SDK_PATH=/tmp/pico-sdk -git clone https://github.com/raspberrypi/picotool.git -cd picotool || exit 1 -sudo cp udev/99-picotool.rules /etc/udev/rules.d/ - -mkdir build -cd build || exit 1 -cmake .. -DCMAKE_BUILD_TYPE=Release -make -j -sudo make install - -cd /tmp || exit 1 -rm -rf pico-sdk -rm -rf picotool - -# Download latest uf2 release, resulting in Telemetrix4RpiPico.uf2 -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -curl -s https://api.github.com/repos/mirte-robot/telemetrix4rpipico/releases/latest | - grep ".*/Telemetrix4RpiPico.uf2" | - cut -d : -f 2,3 | - tr -d \" | - wget -qi - diff --git a/install_bt.sh b/install_bt.sh index 86dff28..f8d67b6 100755 --- a/install_bt.sh +++ b/install_bt.sh @@ -48,5 +48,5 @@ if [ "$(uname -a | grep sunxi)" != "" ]; then install_fake_bt else # Fix for ps3 controller not connecting - sudo sh -c "echo -e '#Fix for ps3 controllers not connecting\nClassicBondedOnly=false\n' >> /etc/bluetooth/input.conf" + sudo sh -c "echo '#Fix for ps3 controllers not connecting\nClassicBondedOnly=false\n' >> /etc/bluetooth/input.conf" fi diff --git a/install_jupyter_ros.sh b/install_jupyter_ros.sh index 4eac751..9beedba 100755 --- a/install_jupyter_ros.sh +++ b/install_jupyter_ros.sh @@ -1,6 +1,6 @@ #!/bin/bash set -xe -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} # install basic python tools sudo apt install -y python3 python3-venv python3-dev git libffi-dev @@ -19,7 +19,7 @@ sudo apt install -y libjpeg-dev zlib1g-dev pip3 install wheel pip3 install markupsafe==2.0.1 pyzmq==24 zipp==3.1.0 \ ipython==8.7.0 ipykernel==6.17.1 ipywidgets==7.7.2 \ - jupyter-client==7.4.8 jupyter-core==5.1.0 \ + typeguard==4.4.2 jupyter-client==7.4.8 jupyter-core==5.1.0 \ nbclient==0.7.2 nbconvert==7.2.6 nbformat==5.7.0 \ qtconsole==5.4.0 traitlets==5.6.0 \ notebook==6.5.2 bqplot==0.12.18 pyyaml \ diff --git a/install_mirte.sh b/install_mirte.sh index 931f61b..44695e7 100755 --- a/install_mirte.sh +++ b/install_mirte.sh @@ -1,7 +1,9 @@ #!/bin/bash set -xe -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} +. $MIRTE_SRC_DIR/settings.sh || true # read settings, like MIRTE_TYPE +MIRTE_TYPE="${MIRTE_TYPE:-default}" # default, mirte-master # disable ipv6, as not all package repositories are available over ipv6 sudo tee /etc/apt/apt.conf.d/99force-ipv4 <> /etc/sudoers' # allow mirte to use sudo without password, needed for auto-shutdown on battery # Install locales sudo apt install -y locales @@ -23,85 +26,101 @@ cp download_repos.sh $MIRTE_SRC_DIR || true cd $MIRTE_SRC_DIR || exit 1 ./download_repos.sh -# Install dependecnies to be able to run python3.8 -sudo apt install -y python3.8 python3-pip python3-setuptools - -# Fix for https://github.com/pypa/setuptools/issues/4478, only in python 3.8 -pip3 install setuptools==70.0.0 +# Install dependencies to be able to run python3 (3.10 default) +sudo apt install -y python3 python3-pip python3-setuptools python3.10-venv # 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 - -# Install Mirte ROS packages and update cmake -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_ROS.sh - -# Install arduino, libs & uploader (nano, stm and pico), requires cmake update from ./install_ROS.sh -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 +cd $MIRTE_SRC_DIR/mirte-install-scripts ./install_arduino.sh +# Install Mirte ROS2 packages +cd $MIRTE_SRC_DIR/mirte-install-scripts +./install_ROS2.sh + # Install Mirte Python package -cd $MIRTE_SRC_DIR/mirte-python || exit 1 -pip3 install . +if [[ "$INSTALL_PYTHON" = true ]]; then + cd $MIRTE_SRC_DIR/mirte-python + pip3 install . +fi # Install Mirte Interface -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_web.sh +if [[ "$INSTALL_WEB" = true ]]; then + cd $MIRTE_SRC_DIR/mirte-install-scripts + ./install_web.sh +fi -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 Jupyter Notebook +if [[ "$INSTALL_JUPYTER" = true ]]; then + cd $MIRTE_SRC_DIR/mirte-install-scripts + ./install_jupyter_ros.sh fi # Install numpy pip3 install numpy # Install bluetooth -cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 -./install_bt.sh +#cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 +#./install_bt.sh + +# if building for mirte-master: +if [[ $MIRTE_TYPE == "mirte-master" ]]; then + + # set default password for root to ... + sudo sed -i '/^root:/d' /etc/shadow + echo 'root:$6$iPpuScKGQTiuJk9r$cBXX/s.8UBp0bvrshHRhw/tHcmU3.beHBfCyJgP8Qhjx2CEO5.dyyvKips6loYQocSTgS/qEYxPrOQd/.qVi70:19793:0:99999:7:::' | sudo tee -a /etc/shadow + # Install Mirte Master + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + ./install_mirte_master.sh +fi # 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 +#cd $MIRTE_SRC_DIR/mirte-documentation +#sudo apt install -y python3-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 +#ln -s $MIRTE_SRC_DIR/mirte-python . +#cd mirte-python +#pip install . +#source /opt/ros/noetic/setup.bash +#source /home/mirte/mirte_ws/devel/setup.bash +#cd ../../ +#make html +#deactivate + +if [[ "$INSTALL_VSCODE" = true ]]; then + cd $MIRTE_SRC_DIR/mirte-install-scripts || exit 1 + ./install_vscode.sh +fi # install audio support to use with mirte-pioneer pcb and orange pi zero 2 sudo apt install pulseaudio libasound2-dev libespeak1 -y -pip3 install simpleaudio pyttsx3 +pip3 install simpleaudio pyttsx3 || true # simpleaudio uses an old python install system. TODO: replace or update # Install overlayfs and make sd card read only (software) sudo apt install -y overlayroot # Currently only instaling, not enabled + +# Install overlayfs (enabling in sd image tools) +# Setup expand overlayfs +{ + # enable mirte-overlay service + sudo rm /lib/systemd/system/mirte-overlay.service || true + sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-overlay.service /lib/systemd/system/ + sudo systemctl enable mirte-overlay.service +} 2>&1 | sed -u 's/^/overlayfs::: /' & + #sudo bash -c "echo 'overlayroot=\"tmpfs\"' >> /etc/overlayroot.conf" +# update time in /etc/fake-hwclock.data +sudo fake-hwclock save + # remove force ipv4 sudo rm /etc/apt/apt.conf.d/99force-ipv4 || true +sudo rm /etc/resolv.conf || true # remove resolv.conf to use the one from the network. diff --git a/install_mirte_master.sh b/install_mirte_master.sh new file mode 100755 index 0000000..6ebf0ff --- /dev/null +++ b/install_mirte_master.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -xe + +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} + +if [[ ${type:=""} != "mirte_orangepi3b" ]]; then + # Fix for wrong sound card + sudo bash -c 'cat <> /etc/asound.conf +defaults.pcm.card 1 +defaults.ctl.card 1 +EOT' + +fi + +cd $MIRTE_SRC_DIR/mirte-install-scripts/mirte-master/usb_switch/ +sudo apt install libgpiod-dev -y +mkdir build +cd build +cmake .. +make -j +sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-usb-switch.service /lib/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl stop mirte-usb-switch.service || /bin/true +sudo systemctl start mirte-usb-switch.service +sudo systemctl enable mirte-usb-switch.service + +sudo ln -s $MIRTE_SRC_DIR/mirte-install-scripts/services/mirte-shutdown.service /lib/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl stop mirte-shutdown.service || /bin/true +sudo systemctl start mirte-shutdown.service +sudo systemctl enable mirte-shutdown.service + +# create a gpio group and add mirte to it. This is needed to access the gpio ports, otherwise only sudo is allowed. +sudo groupadd gpiod +sudo usermod -a -G gpiod mirte +sudo echo '# udev rules for gpio port access through libgpiod +SUBSYSTEM=="gpio", KERNEL=="gpiochip*", GROUP="gpiod", MODE="0660"' | sudo tee /etc/udev/rules.d/60-gpiod.rules +pip install gpiod==1.5.4 # python3.8 version + +# chatgpt node stuff for @chris-pek +pip install gtts playsound openai==0.28.0 sounddevice scipy SpeechRecognition soundfile transformers datasets pyyaml pydub Elevenlabs || true # some strange package versions +pip install numpy==1.23.1 # python3.8 fix + +# mkdir ~/uboot_fix/ +# cd ~/uboot_fix/ +# # audio fix uboot for orange pi 3b +# if [[ ${type:=""} == "mirte_orangepi3b" ]]; then +# wget https://mirte.arend-jan.com/files/fixes/uboot/linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb +# sudo apt install ./linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb +# rm linux-u-boot-orangepi3b-edge_24.2.1_arm64__2023.10-S095b-P0000-H264e-V49ed-B11a8-R448a.deb +# cd ../ +# rm -rf uboot_fix/ +# fi + +# cd ~/mirte_ws/src +# git clone --recurse-submodules https://github.com/arendjan/mirte-telemetrix-cpp.git +cd ~/mirte_ws +source /opt/ros/humble/setup.bash +rosdep install -y --from-paths src/ --ignore-src --rosdistro humble +colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release diff --git a/install_pam.sh b/install_pam.sh new file mode 100755 index 0000000..f693e35 --- /dev/null +++ b/install_pam.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd pam || exit +sudo apt-get install cmake libpam0g-dev -y +mkdir build +cd build || exit 1 +cmake -DCMAKE_BUILD_TYPE=Release .. +make -j +sudo make install -j # requires sudo privileges diff --git a/install_vscode.sh b/install_vscode.sh new file mode 100755 index 0000000..9f4bb4e --- /dev/null +++ b/install_vscode.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -xe +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} + +# VScode in 2 parts: +# - vscode remote server, when using the ssh plugin from another computer +# preloading as the robot (and computer) might not have internet access later +# - 'vscode' website + +# First part: https://github.com/b01/dl-vscode-server +cd $MIRTE_SRC_DIR || exit +mkdir vscode || true +cd vscode || exit +ARCH="x64" +if [[ "$(uname -m)" == "aarch64" ]]; then + ARCH="arm64" +fi +curl -L https://raw.githubusercontent.com/b01/dl-vscode-server/refs/tags/1.0.1/download-vs-code.sh | bash -s -- "linux" $ARCH + +# Second part: +mkdir -p $MIRTE_SRC_DIR/vscode || true +cd $MIRTE_SRC_DIR/vscode || exit +sudo -u mirte bash -c "curl -fsSL https://code-server.dev/install.sh | sh" +sudo -u mirte bash -c "mkdir -p ~/.config/code-server && cp $MIRTE_SRC_DIR/mirte-install-scripts/config/code_server_config.yaml ~/.config/code-server/config.yaml" || true +sudo systemctl enable code-server@mirte.service || true # Added by the code-server install script diff --git a/install_web.sh b/install_web.sh index a39e4d7..28212c8 100755 --- a/install_web.sh +++ b/install_web.sh @@ -1,16 +1,16 @@ #!/bin/bash set -xe -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} # Update -sudo apt update +sudo apt update || true # Install nodeenv 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 +nodeenv --node=18.0.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env # Install web interface . $MIRTE_SRC_DIR/mirte-web-interface/node_env/bin/activate @@ -18,25 +18,39 @@ nodeenv --node=16.2.0 $MIRTE_SRC_DIR/mirte-web-interface/node_env # Install frontend cd $MIRTE_SRC_DIR/mirte-web-interface/vue-frontend || exit 1 npm install . +npm rebuild npm run build +rm -rf node_modules || true # Install backend cd $MIRTE_SRC_DIR/mirte-web-interface/nodejs-backend || exit 1 npm install . +deactivate_node + +cd $MIRTE_SRC_DIR/mirte-web-interface +git clone https://github.com/dheera/rosboard.git --single-branch +sudo pip3 install tornado +sudo pip3 install simplejpeg # recommended, but ROSboard can fall back to cv2 or PIL instead # Install wetty #cd $MIRTE_SRC_DIR/mirte-web-interface #npm -g install wetty -deactivate_node # Install strace for linetrace functionality sudo apt install -y strace -# For rpi4 non-armbian version we need to give ptrace the right permissions -sudo sed -i 's/kernel.yama.ptrace_scope = 1/kernel.yama.ptrace_scope = 0/g' /etc/sysctl.d/10-ptrace.conf +sudo apt install xxd # Install nginx (as reverse proxy to all services) -sudo apt install -y nginx -sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/nginx.conf /etc/nginx/ +sudo apt install -y nginx libnginx-mod-http-auth-pam +sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/nginx.conf /etc/nginx/sites-available/mirte.conf +sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/nginx_login.conf /etc/nginx/nginx_login.conf +sudo ln /etc/nginx/sites-available/mirte.conf /etc/nginx/sites-enabled/ +sudo rm /etc/nginx/sites-enabled/default # otherwise this will catch :80 by default + +# give nginx access to the passwords file for login +sudo usermod -aG shadow www-data + +sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/sites/401.html /var/www/html/ # Add systemd service sudo rm /lib/systemd/system/mirte-web-interface.service || true @@ -45,3 +59,10 @@ sudo systemctl daemon-reload sudo systemctl stop mirte-web-interface || /bin/true sudo systemctl start mirte-web-interface sudo systemctl enable mirte-web-interface + +# install zerotier for better ros2 networking. User will need to manually join the network +curl -s https://install.zerotier.com | sudo bash +# remove zerotier identity as it otherwise will be the same for all devices using the same image +sudo rm /var/lib/zerotier-one/identity.public || true +sudo rm /var/lib/zerotier-one/identity.secret || true +# new identity should be generated on first boot diff --git a/mirte-master/usb_switch/CMakeLists.txt b/mirte-master/usb_switch/CMakeLists.txt new file mode 100644 index 0000000..0886ec7 --- /dev/null +++ b/mirte-master/usb_switch/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10..3.22) +project(mirte_master_usb_switch) + +add_compile_options(-lgpiodcxx) + +add_executable(${PROJECT_NAME} usb_switch.cpp) +target_link_libraries(${PROJECT_NAME} gpiodcxx) diff --git a/mirte-master/usb_switch/readme.md b/mirte-master/usb_switch/readme.md new file mode 100644 index 0000000..a84b714 --- /dev/null +++ b/mirte-master/usb_switch/readme.md @@ -0,0 +1,4 @@ +# Mirte-master usb switch +the orbbec camera must be unpowered at the boot of the OrangePi3b, otherwise it will pull 1A more and not work. +Together with the USB switcher pcb, this will turn on(and off) the camera after boot using libgpiod. +Pin is GPIO4_C3. \ No newline at end of file diff --git a/mirte-master/usb_switch/usb_switch.cpp b/mirte-master/usb_switch/usb_switch.cpp new file mode 100644 index 0000000..8bf936b --- /dev/null +++ b/mirte-master/usb_switch/usb_switch.cpp @@ -0,0 +1,64 @@ + +#include +#include +#include + +#include +#include +#include +#include + +class gpio_pin { +public: + gpio_pin(std::string name); + std::string name; + char block = 'A'; + std::string chip_name = "gpiochip0"; + int block_line = 0; + int line = 0; + gpiod::chip chip; + gpiod::line gpio_line; +}; + +std::vector pins; +void stop_handler(int s) { + for (const auto &pin : pins) { + std::cout << "Turn off " << pin.name << std::endl; + pin.gpio_line.set_value(0); + pin.gpio_line.release(); + } + exit(0); +} + +int main(void) { + struct sigaction sigIntHandler; + + sigIntHandler.sa_handler = stop_handler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + + sigaction(SIGINT, &sigIntHandler, NULL); + sigaction(SIGTERM, &sigIntHandler, NULL); + + gpio_pin pin("GPIO4_C3"); + + pins.push_back(pin); + pin.gpio_line.request( + {"usb_switch", gpiod::line_request::DIRECTION_OUTPUT, 0}, 0); + pin.gpio_line.set_value(0); // Force off before turning on. + sleep(1); + pin.gpio_line.set_value(1); + pause(); +} + +gpio_pin::gpio_pin(std::string pin_name) { + this->name = pin_name; + // name should be GPIOx_yz, with 0<=x<=5, A<=y<=D, 0<=z<=7 + this->chip_name = (std::string)("gpiochip") + pin_name[4]; + this->block = pin_name[6]; + this->block_line = (int)(pin_name[7] - '0'); + this->line = 8 * (this->block - 'A') + this->block_line; + this->chip = ::gpiod::chip(this->chip_name); + this->gpio_line = chip.get_line(this->line); + std::cout << "new pin" << pin_name << std::endl; +} \ No newline at end of file diff --git a/network_install.sh b/network_install.sh index 64ab2c2..4af0f2d 100755 --- a/network_install.sh +++ b/network_install.sh @@ -1,33 +1,41 @@ #!/bin/bash set -xe -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} + +# Fix for bug in systemd-resolved +# (https://askubuntu.com/questions/973017/wrong-nameserver-set-by-resolvconf-and-networkmanager) +# 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' # 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 +# sed -i 's/#DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf # TODO: check this # Install netplan (not installed on armbian) and networmanager (not installed by Raspberry) -sudo apt install -y netplan.io +#sudo apt install -y netplan.io sudo apt install -y network-manager -sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/50-cloud-init.yaml /etc/netplan/ -sudo apt purge -y ifupdown - -# Fix for bug in systemd-resolved -# (https://askubuntu.com/questions/973017/wrong-nameserver-set-by-resolvconf-and-networkmanager) -# 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' +#sudo cp $MIRTE_SRC_DIR/mirte-install-scripts/50-cloud-init.yaml /etc/netplan/ +#sudo netplan apply +#sudo apt purge -y ifupdown # Install wifi-connect MY_ARCH=$(arch) -if [[ $MY_ARCH == "armv7l" ]]; then MY_ARCH="armv7hf"; fi -wget https://github.com/balena-os/wifi-connect/releases/download/v4.11.1/wifi-connect-v4.11.1-linux-"$(echo "$MY_ARCH")".zip -unzip -h || sudo apt install -y unzip -unzip wifi-connect* -sudo mv wifi-connect /usr/local/sbin -rm wifi-connect* - +if [[ "$MY_ARCH" == "armv7l" ]]; then MY_ARCH="rpi"; fi +# TODO: check with armv7 if it works and dynamically download the correct version +# skip if amd64 +if [[ "$MY_ARCH" != "x86_64" ]]; then + if [[ "$MY_ARCH" == "aarch64" ]]; then + wget https://github.com/ArendJan/balena-os-wifi-connect/releases/download/fix-zerotier/wifi-connect-aarch64-unknown-linux-gnu.zip + fi + if [[ "$MY_ARCH" == "???" ]]; then + wget https://github.com/ArendJan/balena-os-wifi-connect/releases/download/fix-zerotier/wifi-connect-armv7-unknown-linux-gnueabihf.zip + fi + unzip wifi-connect* + sudo mv wifi-connect /usr/local/sbin + rm wifi-connect* +fi # 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/ @@ -60,10 +68,11 @@ 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 +# Install usb_ethernet script from EV3 (and apply the patch) 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 sudo chown mirte:mirte $MIRTE_SRC_DIR/mirte-install-scripts/ev3-usb.sh +chmod +x $MIRTE_SRC_DIR/mirte-install-scripts/ev3-usb.sh +patch $MIRTE_SRC_DIR/mirte-install-scripts/ev3-usb.sh -i $MIRTE_SRC_DIR/mirte-install-scripts/fix_scripts/ev3-usb.patch sudo bash -c 'echo "libcomposite" >> /etc/modules' # remove g_serial from modules to let the ev3-usb script enable usb ethernet on the orange pi zero 1 as well. sudo bash -c "sed -i '/g_serial/d' /etc/modules" @@ -81,10 +90,34 @@ sudo bash -c 'echo "Mirte-XXXXXX" > /etc/hostname' sudo chmod 777 /etc/hostname # Fix for wpa_supplicant error -sudo bash -c "echo 'match-device=driver:wlan0' >> /etc/NetworkManager/NetworkManager.conf" - +# sudo bash -c "echo 'match-device=driver:wlan0' >> /etc/NetworkManager/NetworkManager.conf" + +# Fix for the aw859a (Orange Pi Zero2) driver. The wifi crashes when the bluetooth is +# working. We might need to see if bluetooth can be enabled after wifi was started +# correctly. For now, just disabling since we are not using it. +sudo systemctl disable aw859a-bluetooth || /bin/true +sudo systemctl disable bluetooth || /bin/true +sudo bash -c 'echo "install sprdbt_tty /bin/false" > /etc/modprobe.d/disable-sprdbt_tty.conf' +sudo bash -c 'echo "blacklist sprdbt_tty" > /etc/modprobe.d/disable-sprdbt_tty.conf' +sudo update-initramfs -u + +# Disable armbian-led-state +sudo systemctl disable armbian-led-state || /bin/true + +# set some network settings to have ROS2 working better https://autowarefoundation.github.io/autoware-documentation/main/installation/additional-settings-for-developers/network-configuration/dds-settings/#tune-system-wide-network-settings +cat <>/etc/sysctl.d/10-cyclone-max.conf +# Increase the maximum receive buffer size for network packets +net.core.rmem_max=10485760 # 10Mib, default is 208 KiB + +# IP fragmentation settings +net.ipv4.ipfrag_time=3 # in seconds, default is 30 s +net.ipv4.ipfrag_high_thresh=134217728 # 128 MiB, default is 256 KiB +EOF +sudo service procps force-reload # Reboot after kernel panic # The OPi has a fairly unstable wifi driver which might # 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' + +rm -rf /etc/resolv.conf || true # remove resolv.conf to use the one from the network. diff --git a/network_setup.sh b/network_setup.sh index 29b2667..91b6edc 100755 --- a/network_setup.sh +++ b/network_setup.sh @@ -1,4 +1,18 @@ #!/bin/bash +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} + +($MIRTE_SRC_DIR/mirte-install-scripts/services/mirte_auto_dhcp_eth.sh || true) & + +sleep 20 # wait some time for wifi driver to not crash. + +# wait for wlan0 to be up, with max 30 seconds +TIMEOUT=30 +NEXT_WAIT_TIME=0 +until [ $NEXT_WAIT_TIME -eq $TIMEOUT ] || ip addr show "wlan0"; do + echo "waiting for wlan0" + sleep 1 + let "NEXT_WAIT_TIME=NEXT_WAIT_TIME+1" +done function start_avahi { # Restart avahi-daemon, to clear all previous addresses and hosts @@ -20,13 +34,6 @@ function start_acces_point { sudo killall -9 blink.sh || /bin/true echo "Killed all previous instances" - # It takes some time for NetworkManager to find all - # networks. - nmcli con down "$(cat /etc/hostname)" - iw dev wlan0 scan | grep SSID - nmcli device wifi list - echo "Rescanned networks" - # Start wifi-connect (this starts the AP, and uses dnsmasq # as DHCP server wifi-connect -o 8080 -p "$(cat /home/mirte/.wifi_pwd)" -s "$(cat /etc/hostname)" & @@ -42,8 +49,9 @@ function start_acces_point { nmcli con modify "$(cat /etc/hostname)" 802-11-wireless-security.proto wpa # nmcli con modify `cat /etc/hostname` 802-11-wireless-security.group ccmp # nmcli con modify `cat /etc/hostname` 802-11-wireless-security.pairwise ccmp + # generate a random channel, can even change to 5Ghz if wanted, then check how to find a correct channel + nmcli c modify "$(cat /etc/hostname)" 802-11-wireless.band bg 802-11-wireless.channel "$(shuf -i 1-11 -n 1)" nmcli con down "$(cat /etc/hostname)" - sleep 10 nmcli con up "$(cat /etc/hostname)" # Start all avahi addresses and services @@ -74,11 +82,14 @@ function check_connection { # Get wifi connection if connected if sudo nmcli con show --active | grep wlan0; then - # Bugfix (see network_install.sh) - sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf printf 'Connected to wifi connection:' nmcli con show --active | grep wlan0 + while [ -z "$(hostname -I)" ]; do + echo "Waiting for IP address..." + sleep 1 + done + echo "Got IP: $(hostname -I)" $MIRTE_SRC_DIR/mirte-install-scripts/blink.sh "$(hostname -I)" & start_avahi else @@ -125,17 +136,35 @@ function file_empty() { } -MIRTE_SRC_DIR=/usr/local/src/mirte - -$MIRTE_SRC_DIR/mirte-install-scripts/usb_ethernet.sh +sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf || true # Create unique SSID # This must be run every time on boot, since it should # be generated on first boot (so not when generating # the image in network_setup.sh) -if [ ! -f /etc/ssid ] || [[ $(cat /etc/hostname) == "Mirte-XXXXXX" ]]; then +USE_MAC=true +if ! ip addr show "wlan0" | grep -q ether; then + USE_MAC=false + echo "No MAC address found, using random ID" +fi +if $USE_MAC; then # when using mac for hostname. + mac=$(ip addr show "wlan0" | awk '/ether/{print $2}') + UNIQUE_ID=$(echo -n $mac | tr -cd "1-9A-Fa-f" | tail -c 6) # last 6 characters of mac address, without colons or 0s + MIRTE_SSID="Mirte-$(echo ${UNIQUE_ID^^})" + echo "Generated SSID: $MIRTE_SSID" + # add to check: || [[ "$(cat /etc/hostname)" != "$MIRTE_SSID" ]] +else + # When no MAC address is found, a random ID is generated + # This should be changed to a unique ID UNIQUE_ID=$(tr -cd "1-9A-F" /etc/hostname' sudo ln -s /etc/hostname /etc/ssid # And add them to the hosts file @@ -147,6 +176,8 @@ fi check_ssh_host_keys +$MIRTE_SRC_DIR/mirte-install-scripts/usb_ethernet.sh + check_connection # This should run forever, otherwise systemd will shut it down diff --git a/nginx.conf b/nginx.conf index ca6877b..7509085 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,99 +1,144 @@ -user www-data; -worker_processes auto; -pid /run/nginx.pid; -include /etc/nginx/modules-enabled/*.conf; - -events { - worker_connections 768; - # multi_accept on; -} - -http { - +server { - server { - - gzip_min_length 1000; - gzip_buffers 4 8k; - gzip_http_version 1.0; - gzip_disable "msie6"; - gzip_types text/plain text/css application/javascript; - gzip_vary on; - gzip on; - gzip_proxied any; + gzip_min_length 1000; + gzip_buffers 4 8k; + gzip_http_version 1.0; + gzip_disable "msie6"; + gzip_types text/plain text/css application/javascript; + gzip_vary on; + gzip on; + gzip_proxied any; # The NodeJs (Vue) webserver location / { + include /etc/nginx/nginx_login.conf; + proxy_pass http://localhost:3000/; } # The ROS video server location /ros-video/ { - proxy_pass http://localhost:8181/; + include /etc/nginx/nginx_login.conf; + + proxy_pass http://localhost:8181/; + sub_filter "/snapshot" "/ros-video/snapshot"; # convert all /snapshot... texts(url) to /ros-video/snapshot... + sub_filter "/stream" "/ros-video/stream"; # convert /stream... urls to /ros-video/stream... Used for the stream_viewer page and actual stream. + sub_filter_once off; + sub_filter_types "*"; } # Jupyter Notebook location /jupyter/ { - proxy_pass http://localhost:8888/; + include /etc/nginx/nginx_login.conf; + + proxy_pass http://localhost:8888/jupyter/; } # Wifi Connect location /wifi-connect/ { - # Wifi connect does not map to localhost - proxy_pass http://192.168.42.1:8080/; + include /etc/nginx/nginx_login.conf; + + # Wifi connect does not map to localhost + proxy_pass http://192.168.42.1:8080/; } # Sphinx RTD Docs location /docs/ { - include /etc/nginx/mime.types; + # TODO: do we need login for this? + # TODO: docs compilation is broken? + include /etc/nginx/mime.types; alias /usr/local/src/mirte/mirte-documentation/_build/html/; } # ROS bridge websocket location /ws/ros { - proxy_pass http://localhost:9090; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; + include /etc/nginx/nginx_login.conf; + + proxy_pass http://localhost:9090/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; } # Shell websocket location /ws/shell { + include /etc/nginx/nginx_login.conf; + proxy_pass http://localhost:3000/shell; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; } # Linetrace location /ws/linetrace { + include /etc/nginx/nginx_login.conf; + proxy_pass http://localhost:8001; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; } # mDNS client discovery location /ws/clients { + include /etc/nginx/nginx_login.conf; + proxy_pass http://localhost:4567; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; } - } + # VSCode system: + # /code/ will redirect to :8000 as vscode won't work nicely in a folder + # + location /code/ { + return 301 http://$host:8000/; # that is defined lower + } + # custom page when logging in has failed + error_page 401 /error/401.html; + location /error/ { + alias /var/www/html/; + } } +# VSCode server: +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen *:8000; # vscode does not work in a folder + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Host $http_host; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + location / { + include /etc/nginx/nginx_login.conf; + + proxy_pass http://127.0.0.1:9000/; + } + + error_page 401 /error/401.html; # custom page when logging in has failed + location /error/ { + alias /var/www/html/; + } +} diff --git a/nginx_login.conf b/nginx_login.conf new file mode 100644 index 0000000..0be7c3d --- /dev/null +++ b/nginx_login.conf @@ -0,0 +1,6 @@ +satisfy any; +allow 192.168.40.0/21; # 40.1 - 47.1 +allow 192.168.137.0/24; +# allow 192.168.2.0/24; +auth_pam "Authentication Required"; +auth_pam_service_name "nginx"; \ No newline at end of file diff --git a/pam/.gitignore b/pam/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/pam/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/pam/CMakeLists.txt b/pam/CMakeLists.txt new file mode 100644 index 0000000..a1dc633 --- /dev/null +++ b/pam/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.10..3.22) +project(mirte_pam) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") + +find_package(PAM) + +# Check if we found PAM. +if(NOT PAM_FOUND) + message( + FATAL_ERROR + "PAM library was not found. Install: sudo apt-get install libpam0g-dev") +endif() + +include_directories(${PAM_INCLUDE_DIR} ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}) + +set(modules warn storepassword) + +foreach(module ${modules}) + add_library(mirte_pam_${module} SHARED ${module}.c) + + install(TARGETS mirte_pam_${module} DESTINATION /lib/security/) + +endforeach(module ${modules}) + +add_executable(myprogram test.c storepassword.c) +install( + CODE "execute_process(COMMAND bash ${CMAKE_SOURCE_DIR}/install_module.sh)") diff --git a/pam/cmake/modules/FindPAM.cmake b/pam/cmake/modules/FindPAM.cmake new file mode 100644 index 0000000..4c8db27 --- /dev/null +++ b/pam/cmake/modules/FindPAM.cmake @@ -0,0 +1,76 @@ +# - Try to find the PAM libraries +# Once done this will define +# +# PAM_FOUND - system has pam +# PAM_INCLUDE_DIR - the pam include directory +# PAM_LIBRARIES - libpam library +# +# SPDX-License-Identifier: BSD-3-Clause +# Copied from https://github.com/KDE/kwallet-pam/blob/master/cmake/modules/FindPAM.cmake +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + # Already in cache, be silent + set(PAM_FIND_QUIETLY TRUE) +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) +find_library(PAM_LIBRARY pam) +find_library(DL_LIBRARY dl) + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + set(PAM_FOUND TRUE) + if (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY}) + else (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY}) + endif (DL_LIBRARY) + + if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + # darwin claims to be something special + set(HAVE_PAM_PAM_APPL_H 1) + endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + + if (NOT DEFINED PAM_MESSAGE_CONST) + include(CheckCXXSourceCompiles) + # XXX does this work with plain c? + check_cxx_source_compiles(" +#if ${HAVE_PAM_PAM_APPL_H}+0 +# include +#else +# include +#endif + +static int PAM_conv( + int num_msg, + const struct pam_message **msg, /* this is the culprit */ + struct pam_response **resp, + void *ctx) +{ + return 0; +} + +int main(void) +{ + struct pam_conv PAM_conversation = { + &PAM_conv, /* this bombs out if the above does not match */ + 0 + }; + + return 0; +} +" PAM_MESSAGE_CONST) + endif (NOT DEFINED PAM_MESSAGE_CONST) + set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message") + +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +if (PAM_FOUND) + if (NOT PAM_FIND_QUIETLY) + message(STATUS "Found PAM: ${PAM_LIBRARIES}") + endif (NOT PAM_FIND_QUIETLY) +else (PAM_FOUND) + if (PAM_FIND_REQUIRED) + message(FATAL_ERROR "PAM was not found") + endif(PAM_FIND_REQUIRED) +endif (PAM_FOUND) + +mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST) \ No newline at end of file diff --git a/pam/image.png b/pam/image.png new file mode 100644 index 0000000..e595960 Binary files /dev/null and b/pam/image.png differ diff --git a/pam/install_module.sh b/pam/install_module.sh new file mode 100755 index 0000000..49cdd3f --- /dev/null +++ b/pam/install_module.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +#check if already added to these settings and put it in the correct place if not yet +echo "installing mirte pam modules when needed" +if ! grep -q libmirte_pam_warn "/etc/pam.d/passwd"; then + sed -i '/@include common-password/i \ +password required /lib/security/libmirte_pam_warn.so' /etc/pam.d/passwd + echo "added warning module to /etc/pam.d/passwd" +fi + +if ! grep -q libmirte_pam_storepassword "/etc/pam.d/common-password"; then + printf "\npassword required /lib/security/libmirte_pam_storepassword.so\n" >>/etc/pam.d/common-password + echo "added password storing to /etc/pam.d/common-password" +fi diff --git a/pam/mirte_pam.h b/pam/mirte_pam.h new file mode 100644 index 0000000..09d95af --- /dev/null +++ b/pam/mirte_pam.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PAM_MOCK +int pam_get_item(void *p, int i, const void **pwd) { return 0; } +#else +#include +#include +#endif +#define RED "\x1B[31m" +#define GRN "\x1B[32m" +#define YEL "\x1B[33m" +#define BLU "\x1B[34m" +#define MAG "\x1B[35m" +#define CYN "\x1B[36m" +#define WHT "\x1B[37m" +#define RESET "\x1B[0m" + +#define wifi_password_folder "/home/mirte/" +#define wifi_filename wifi_password_folder ".wifi_pwd" +#define mirte_username "mirte" +void savePassword(char *, char *); +int checkDirectory(); diff --git a/pam/readme.md b/pam/readme.md new file mode 100644 index 0000000..11f3040 --- /dev/null +++ b/pam/readme.md @@ -0,0 +1,46 @@ +# password module + +This system will show some info to the user when changing passwords and stores the new password in `/home/mirte/.wifi_pwd`. The warnings are hooked in to `passwd` by using pam modules. It will only print and store when the mirte user password is changed, not when changing the root password. + +Output: + +![passwd with mirte pam](image.png) + +# Build +Building PAM modules requires `libpam0g-dev`: + +```sh +sudo apt-get install libpam0g-dev +``` + +Building the Mirte PAM system: +```sh +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +make +make install # requires sudo privileges +``` + +This will build the Mirte pam modules(warning and storepassword), installs them to `/lib/security/limbirte_pam_xxx.so` and add the required lines to `/etc/pam.d/passwd` and `/etc/pam.d/common-password`. + +### passwd: +The resulting `/etc/pam.d/passwd` should look like: +``` +# +# The PAM configuration file for the Shadow `passwd' service +# + +password required /lib/security/libmirte_pam_warn.so +@include common-password +``` + +### common-password: +The resulting `/etc/pam.d/common-password` should look like: + +``` +... +password required /lib/security/libmirte_pam_storepassword.so +``` + +The `warn` module is started before the actual passwd system, so the output will be before the passwd output. The storepassword is at the end to catch the plaintext password. \ No newline at end of file diff --git a/pam/storepassword.c b/pam/storepassword.c new file mode 100644 index 0000000..6283eb9 --- /dev/null +++ b/pam/storepassword.c @@ -0,0 +1,80 @@ +#define PAM_SM_PASSWORD 1 +#include "mirte_pam.h" +#include +#include +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, + const char **argv) { + if (flags & PAM_PRELIM_CHECK) { + return PAM_SUCCESS; + } + char *pwd; + char *user; + int r = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&pwd); + pam_get_item(pamh, PAM_USER, (const void **)&user); + savePassword(user, pwd); + return PAM_SUCCESS; +} + +void savePassword(char *username, char *passwd) { + // Don't show anything when it is not the mirte user + if (strcmp(username, mirte_username) != 0) { + return; + } + // filter out string with non-ASCII characters, only allow a-zA-Z0-9 + // and some special characters + char *new_passwd = (char *)malloc(strlen(passwd) + 1); + if (new_passwd == NULL) { + printf(RED "Mirte:\t" RESET "Memory allocation failed.\n"); + return; + } + int new_passwd_i = 0; + for (int i = 0; i < strlen(passwd); i++) { + if (isalpha(passwd[i]) || isdigit(passwd[i]) || passwd[i] == '-' || + passwd[i] == '_') { + new_passwd[new_passwd_i] = passwd[i]; + new_passwd_i++; + } + } + new_passwd[new_passwd_i] = '\0'; + if (new_passwd_i < 8) { + printf(RED "Mirte:\t" RESET "Password is too short, not storing " + "password for Wi-Fi.\n"); + free(new_passwd); + return; + } + printf(GRN + "Mirte:\t" RESET "The new password for \"%s\" is \"%s\".\n" GRN + "Mirte:\t" RESET "The password will be updated for the webpages.\n" GRN + "Mirte:\t" RESET "The Wi-Fi password will be updated at next boot!\n", + username, passwd); + if (strlen(new_passwd) != strlen(passwd)) { + printf(RED "Mirte:\t" RESET + "Original password contained non-ASCII characters, they are " + "removed from the Wi-Fi password.\n" RED "Mirte:\t" RESET + "New Wi-Fi password is \"%s\".\n", + new_passwd); + free(new_passwd); + return; + } + if (!checkDirectory()) { + printf(RED "Mirte:\t" RESET + "Mirte home directory does not exist, not storing " + "password for Wi-Fi.\n"); + return; + } + FILE *file = fopen(wifi_filename, "w"); + fprintf(file, "%s", new_passwd); + fclose(file); +} + +int checkDirectory() { + DIR *dir = opendir(wifi_password_folder); + if (dir) { + /* Directory exists. */ + closedir(dir); + return 1; + } else // ENOENT or any other value + { + return 0; + } +} \ No newline at end of file diff --git a/pam/test.c b/pam/test.c new file mode 100644 index 0000000..45dce0a --- /dev/null +++ b/pam/test.c @@ -0,0 +1,14 @@ +#define PAM_MOCK +#include "mirte_pam.h" + +int main() { + // will just update the file + printf("Not mirte user test:\n"); + savePassword("asdf", "ww"); + printf("Mirte too short user test:\n"); + savePassword("mirte", "ww"); + printf("Mirte user test:\n"); + savePassword("mirte", "ww12345677"); + printf("Mirte user test strange chars:\n"); + savePassword("mirte", "ww12345677!@#$%^&*()_+"); +} \ No newline at end of file diff --git a/pam/warn.c b/pam/warn.c new file mode 100644 index 0000000..063f23e --- /dev/null +++ b/pam/warn.c @@ -0,0 +1,27 @@ +#define PAM_SM_PASSWORD 1 +#include "mirte_pam.h" +/* + This module will warn users when changing their password that it is stored + in plaintext Compile with gcc -fPIC -fno-stack-protector -c warn.c && ld -x + --shared -o /lib/security/warn.so warn.o and add password required + /lib/security/warn.so before the "@include common-password" line in + /etc/pam.d/passwd to let it run before the passwd system. +*/ +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, + const char **argv) { + if (flags == PAM_PRELIM_CHECK) { + char *user; + pam_get_item(pamh, PAM_USER, (const void **)&user); + // Don't show anything when it is not the mirte user + if (strcmp(user, mirte_username) != 0) { + return PAM_SUCCESS; + } + printf(GRN + "Mirte:\t" RESET "Your password will be stored in plaintext, so " + "don't make it your super secret password!\n" GRN "Mirte:\t" RESET + "The web-login and the Mirte-hotspot will get the same password.\n"); + return PAM_SUCCESS; + } + + return PAM_SUCCESS; +} \ No newline at end of file diff --git a/repos.yaml b/repos.yaml index 3dbf8a1..025234d 100644 --- a/repos.yaml +++ b/repos.yaml @@ -2,40 +2,38 @@ repositories: mirte-web-interface: type: git url: https://github.com/mirte-robot/mirte-web-interface.git - version: main + version: develop mirte-telemetrix4arduino: type: git url: https://github.com/mirte-robot/Telemetrix4Arduino.git - version: master + version: develop mirte-install-scripts: type: git url: https://github.com/mirte-robot/mirte-install-scripts.git - version: main - mirte-telemetrix-aio: - type: git - url: https://github.com/mirte-robot/telemetrix-aio.git - version: master - mirte-tmx-pico-aio: - type: git - url: https://github.com/mirte-robot/tmx-pico-aio.git - version: master + version: develop mirte-ros-packages: type: git url: https://github.com/mirte-robot/mirte-ros-packages.git - version: main + version: develop + recursive: true + mirte-gazebo: + type: git + url: https://github.com/mirte-robot/mirte-gazebo.git + version: develop mirte-oled-images: type: git url: https://github.com/mirte-robot/mirte-oled-images.git version: main - mirte-arduino-libraries: - type: git - url: https://github.com/mirte-robot/mirte-arduino-libraries.git - version: main mirte-python: type: git url: https://github.com/mirte-robot/mirte-python.git - version: main + version: develop mirte-documentation: type: git url: https://github.com/mirte-robot/mirte-documentation.git version: main + mirte-telemetrix4rpipico: + type: git + url: https://github.com/mirte-robot/Telemetrix4RpiPico.git + recursive: true + version: develop diff --git a/run-avrdude b/run-avrdude deleted file mode 100755 index bd38927..0000000 --- a/run-avrdude +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -#TODO: this now only works for STM, also make nano work -#TODO: maybe merge this into run.sh from aruino repo? - -# Stop ROS when uploading new code -sudo service mirte-ros stop - -if test "$1" == "upload"; then - sudo arduino-cli upload -p /dev/ttyACM0 -b STM32:stm32:GenF1:pnum=BLACKPILL_F103C8,upload_method=dfu2Method,xserial=generic,usb=CDCgen,xusb=FS,opt=osstd,rtlib=nano -i /home/mirte/firmware.bin -else - objcopy --input-target=ihex --output-target=binary /tmp/sketch.hex /tmp/sketch.bin - runuser -l mirte -c 'sudo arduino-cli upload -p /dev/ttyACM0 -b STM32:stm32:GenF1:pnum=BLACKPILL_F103C8,upload_method=dfu2Method,xserial=generic,usb=CDCgen,xusb=FS,opt=osstd,rtlib=nano -i /tmp/sketch.bin' -fi diff --git a/run_arduino.sh b/run_arduino.sh index 231357a..e36fcc0 100755 --- a/run_arduino.sh +++ b/run_arduino.sh @@ -1,59 +1,125 @@ #!/bin/bash +# set -x +#TODO: script should have format ./run.sh build|upload] mcu_type +COMMAND=$1 +if [ -z "$COMMAND" ]; then + echo "Usage: $0 build_[mcu] | upload_[mcu]" + echo "Example: $0 build_nano" + echo "MCU types: nano, nano_old, pico, stm32" + exit 1 +fi -#TODO: script should have format ./run.sh build|upload] mcu_type arduino_folder -# with mcu_type and arduino_folder optional +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} +export INSTALL_ARDUINO_ALL=${INSTALL_ARDUINO_ALL:-false} +source $MIRTE_SRC_DIR/settings.sh || true +PICO_BUILD_LOCATION=$MIRTE_SRC_DIR/mirte-telemetrix4rpipico/build/Telemetrix4RpiPico # Check if ROS is running -ROS_RUNNING=$(ps aux | grep -c "[r]osmaster") - +ROS_RUNNING=0 +systemctl is-active mirte-ros | grep 'active' &>/dev/null +if [ $? == 0 ]; then + ROS_RUNNING=1 +fi +echo "ROS_RUNNING: $ROS_RUNNING" +PROJECT="mirte-telemetrix4arduino" # Stop ROS when uploading new code STOPPED_ROS=false -if [[ $1 == upload* ]] && [[ $ROS_RUNNING == "1" ]]; then # test for any upload... command +if [[ $COMMAND == upload* ]] && [[ $ROS_RUNNING == "1" ]]; then # test for any upload... command echo "STOPPING ROS" sudo service mirte-ros stop || /bin/true STOPPED_ROS=true fi +cd $MIRTE_SRC_DIR/$PROJECT || exit 1 -# 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 -fi -if test "$1" == "build_nano"; then - 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 -fi -if test "$1" == "build_uno"; then - arduino-cli -v compile --fqbn arduino:avr:uno /home/mirte/arduino_project/$2 -fi +buildpico() { + if [ "$INSTALL_ARDUINO_ALL" != true ]; then + echo "Using prebuilt uf2, set INSTALL_ARDUINO_ALL to true to build from source" + return + fi + cd $MIRTE_SRC_DIR/mirte-telemetrix4rpipico || exit 1 + # shellcheck disable=SC2164 + mkdir -p build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Debug + make +} -# 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 -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 -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 -fi -if test "$1" == "upload_uno"; then - arduino-cli -v upload -p /dev/ttyACM0 --fqbn arduino:avr:uno /home/mirte/arduino_project/$2 -fi +upload_pico_uart() { + for port in /dev/serial/by-id/*; do # these are only the usb serial ports, not all the other uart ports. + port=$(realpath $port) + # send reboot command + stty 115200 -F $port + echo -ne '\x01\x26' >$port # 1 byte message, message id 0x26==reset_to_bootloader + sleep 3 + # try to upload + ERR=false + pico_py_serial_flasher $port $PICO_BUILD_LOCATION.elf || ERR=true + if $ERR; then + echo "Failed to upload to Pico using pico_py_serial_flash port $port" + else + echo "Successfully uploaded to Pico using pico_py_serial_flash port $port" + return 0 + fi + done + return 1 +} -if test "$1" == "upload_pico"; then - MIRTE_SRC_DIR=/usr/local/src/mirte - # This will always upload telemetrix4rpipico.uf2, so no need to pass a file - sudo picotool load -f $MIRTE_SRC_DIR/mirte-install-scripts/Telemetrix4RpiPico.uf2 - retVal=$? - if [ $retVal -ne 0 ]; then - echo "Failed to upload to Pico" - echo "Please check the connection and try again" - echo "Or unplug the Pico, press the BOOTSEL button and plug it in again" +# Different build scripts +if [[ $COMMAND == build* ]]; then + if test "$COMMAND" == "build"; then + echo "Building all versions, this will take a while and might need internet connection for tools" + pio run + elif test "$COMMAND" == "build_nano"; then + pio run -e nanoatmega328new + elif test "$COMMAND" == "build_nano_old"; then + echo "Building non-default mcu, might need internet connection for tools" + pio run -e nanoatmega328 + elif test "$COMMAND" == "build_pico"; then + buildpico + else + echo "Unknown build command $COMMAND" exit 1 fi - sudo picotool reboot # just to make sure, sometimes it does not reboot automatically +elif [[ $COMMAND == upload* ]]; then + # Different upload scripts + if test "$COMMAND" == "upload" || test "$COMMAND" == "upload_stm32"; then + echo "Uploading to non-default mcu, might need internet connection for tools" + pio run -e robotdyn_blackpill_f303cc -t upload + elif test "$COMMAND" == "upload_nano"; then + pio run -e nanoatmega328new -t upload + elif test "$COMMAND" == "upload_nano_old"; then + echo "Uploading to non-default mcu, might need internet connection for tools" + pio run -e nanoatmega328 -t upload + elif test "$1" == "upload_pico"; then + buildpico + # This will always upload telemetrix4rpipico.uf2, so no need to pass a file + ERR=false + sudo picotool load -f $PICO_BUILD_LOCATION.uf2 || ERR=true + sleep 2 + sudo picotool verify -f $PICO_BUILD_LOCATION.uf2 && ERR=false || ERR=true # if verifying is okay, then it's also good + sleep 2 + # if lsusb has pico boot, then run reboot + if lsusb | grep -q "Raspberry Pi RP2 Boot"; then + sudo picotool reboot + fi + + if $ERR; then + echo "Failed to upload to Pico using picotool, trying using uart" + upload_pico_uart + FAILED=$? + if [ $FAILED -eq 1 ]; then + echo "Failed to upload using uart." + echo "Please check the connection and try again" + echo "Or unplug the Pico, press the BOOTSEL button and plug it in again" + exit 1 + fi + fi + else + echo "Unknown upload command $COMMAND" + exit 1 + fi +else + echo "Unknown command $COMMAND" + exit 1 fi # Start ROS again diff --git a/services/mirte-ap.service b/services/mirte-ap.service index a860243..7d46763 100644 --- a/services/mirte-ap.service +++ b/services/mirte-ap.service @@ -1,10 +1,21 @@ [Unit] Description=Mirte Wifi AP -After=NetworkManager.service -After=network-online.target +Wants=multi-user.target +After=multi-user.target [Service] KillMode=process +# The orange pi (zero 2) has a flacky wifi chip. This means we have to wait +# for this AP to start. The result below are better when we fully disable +# bluetooth in the network_install.sh script. +# This is probably related to: https://github.com/orangepi-xunlong/firmware/issues/1 + +# Testing this setup: +# sleep 0: 99/100 failed +# sleep 10: 50/100 failed +# sleep 20: 31/100 failed +# sleep 30: 0/100 failed +ExecStartPre=/bin/sleep 30 ExecStart=/bin/bash -c "/usr/local/src/mirte/mirte-install-scripts/network_setup.sh" [Install] diff --git a/services/mirte-jupyter.service b/services/mirte-jupyter.service index 2bbeb28..743102f 100644 --- a/services/mirte-jupyter.service +++ b/services/mirte-jupyter.service @@ -6,7 +6,7 @@ After=network-online.target [Service] User=mirte -ExecStart=/bin/bash -c "source /opt/ros/noetic/setup.bash && source /home/mirte/mirte_ws/devel/setup.bash && cd /home/mirte/jupyter-ros/notebooks && source /home/mirte/jupyter/bin/activate && jupyter nbextension enable --py --sys-prefix jupyros && jupyter notebook --ip='0.0.0.0' --NotebookApp.token='' --NotebookApp.password=''" +ExecStart=/bin/bash -c "source /opt/ros/noetic/setup.bash && source /home/mirte/mirte_ws/devel/setup.bash && cd /home/mirte/jupyter-ros/notebooks && source /home/mirte/jupyter/bin/activate && jupyter nbextension enable --py --sys-prefix jupyros && jupyter notebook --ip='0.0.0.0' --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.base_url='/jupyter/'" [Install] WantedBy=multi-user.target diff --git a/services/mirte-master-ros.service b/services/mirte-master-ros.service new file mode 100644 index 0000000..b267cfb --- /dev/null +++ b/services/mirte-master-ros.service @@ -0,0 +1,19 @@ +[Unit] +Description=Mirte ROS +After=network.target +After=ssh.service +After=network-online.target + +StartLimitIntervalSec=500 +StartLimitBurst=5 + +[Service] +Restart=always +RestartSec=5s +User=mirte +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_ros.sh minimal_master +TimeoutStopSec=5s +#Need to be done due to bug https://github.com/RobotWebTools/rosbridge_suite/issues/1024 + +[Install] +WantedBy=multi-user.target diff --git a/services/mirte-overlay.service b/services/mirte-overlay.service new file mode 100644 index 0000000..7dab1e9 --- /dev/null +++ b/services/mirte-overlay.service @@ -0,0 +1,12 @@ +[Unit] +Description=Mirte-overlay +After=mirte-ros.service + +[Service] +Type=oneshot +RemainAfterExit=false +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_expandoverlay.sh +TimeoutSec=30 +[Install] +WantedBy=multi-user.target +WantedBy=mirte-ros.service diff --git a/services/mirte-ros.service b/services/mirte-ros.service index fa652ef..14341ae 100644 --- a/services/mirte-ros.service +++ b/services/mirte-ros.service @@ -4,9 +4,16 @@ After=network.target After=ssh.service After=network-online.target +StartLimitIntervalSec=500 +StartLimitBurst=5 + [Service] -User=mirte -ExecStart=/bin/bash -c "source /home/mirte/mirte_ws/devel/setup.bash && roslaunch mirte_bringup minimal.launch" +Restart=always +RestartSec=5s +User=mirte +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_ros.sh +TimeoutStopSec=5s +#Need to be done due to bug https://github.com/RobotWebTools/rosbridge_suite/issues/1024 [Install] WantedBy=multi-user.target diff --git a/services/mirte-shutdown.service b/services/mirte-shutdown.service new file mode 100755 index 0000000..fa425d8 --- /dev/null +++ b/services/mirte-shutdown.service @@ -0,0 +1,14 @@ +[Unit] +Description=Mirte-shutdown +After=mirte-ros.service + +[Service] +User=mirte +Type=oneshot +RemainAfterExit=true +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_boot.sh +ExecStop=/usr/local/src/mirte/mirte-install-scripts/services/mirte_shutdown.sh +TimeoutSec=30 +[Install] +WantedBy=multi-user.target +WantedBy=mirte-ros.service diff --git a/services/mirte-usb-switch.service b/services/mirte-usb-switch.service new file mode 100755 index 0000000..bb375f7 --- /dev/null +++ b/services/mirte-usb-switch.service @@ -0,0 +1,12 @@ +[Unit] +Description=Mirte USB switch +After=network.target +After=ssh.service +After=network-online.target + +[Service] +User=root +ExecStart=/bin/bash -c "/usr/local/src/mirte/mirte-install-scripts/mirte-master/usb_switch/build/mirte_master_usb_switch" + +[Install] +WantedBy=multi-user.target diff --git a/services/mirte-web-interface.service b/services/mirte-web-interface.service index 116f4a2..458768c 100644 --- a/services/mirte-web-interface.service +++ b/services/mirte-web-interface.service @@ -7,7 +7,7 @@ After=network-online.target [Service] User=mirte # We need bash in bash since ROS noetic and NPM do not work nicely together -ExecStart=/bin/bash -c '/bin/bash -c "source /home/mirte/mirte_ws/devel/setup.bash && python3 -m mirte_robot.linetrace" & /bin/bash -c "cd /usr/local/src/mirte/mirte-web-interface/ && source ./node_env/bin/activate && cd nodejs-backend && npm run backend"' +ExecStart=/usr/local/src/mirte/mirte-install-scripts/services/mirte_web_interface.sh # And we need a tty to strace it StandardInput=tty-force TTYVHangup=yes diff --git a/services/mirte_auto_dhcp_eth.sh b/services/mirte_auto_dhcp_eth.sh new file mode 100755 index 0000000..4d93544 --- /dev/null +++ b/services/mirte_auto_dhcp_eth.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# nm dispatcher is whack and not running reliably or without the interface name. + +# this script repeatedly checks eth0, when it goes up, wait for a few seconds +# if no ip assigned: start dnsmasq +# if ip assigned:don't do anything +# when eth0 goes down, kill dnsmasq if started + +set -xe + +# start dnsmasq +SUBNET=45 +interface=eth0 + +dnsmasq_pid=$(pgrep -f "dnsmasq --address=/#/192.168.$SUBNET.1") || true + +function cleanup { + has_dhcp=false + echo "Cleaning up..." + if [ -n "$dnsmasq_pid" ]; then + echo "Stopping dnsmasq with PID $dnsmasq_pid" + sudo kill "$dnsmasq_pid" || true + dnsmasq_pid="" + else + echo "No dnsmasq process found." + fi + sudo ip address del 192.168."$SUBNET".1/24 dev eth0 || true + # exit 0 + echo "Cleanup complete." + # exit 0 +} + +function start_dhcp { + cleanup + has_dhcp=true + sudo ip address add 192.168."$SUBNET".1/24 dev eth0 + sudo dnsmasq --address=/#/192.168."$SUBNET".1 --dhcp-range=192.168."$SUBNET".10,192.168."$SUBNET".100 --conf-file --domain-needed --bogus-priv --server=8.8.8.8 --dhcp-option=option:dns-server,8.8.8.8 --interface="$interface" --except-interface=lo --bind-interfaces -p0 --dhcp-leasefile=/tmp/dnsmasq.leases + dnsmasq_pid=$(pgrep -f "dnsmasq --address=/#/192.168.$SUBNET.1") +} + +function check_up { + is_up=$(ip addr show $interface | grep -q "state UP" && echo true || echo false) + has_ip=$(ip addr show $interface | grep -q "inet " && echo true || echo false) + if [ "$is_up" = false ]; then + SECONDS=0 + elif [ $SECONDS -gt 10 ] && [ "$has_ip" = false ]; then + echo "Interface $interface has been up for more than 10 seconds." + echo "No IP address assigned, starting DHCP server." + start_dhcp + fi +} + +function check_down { + is_up=$(ip addr show $interface | grep -q "state UP" && echo true || echo false) + if [ "$is_up" = false ] && [ "$has_dhcp" = true ]; then + echo "Interface $interface is down, stopping DHCP server." + cleanup + fi +} + +trap cleanup EXIT + +is_up=false +has_dhcp=false +while true; do + check_up + check_down + sleep 5 +done diff --git a/services/mirte_boot.sh b/services/mirte_boot.sh new file mode 100755 index 0000000..44bf74c --- /dev/null +++ b/services/mirte_boot.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -xe +# check if file /home/mirte/shutdown exists +if [ -f /home/mirte/.shutdown ]; then + # check if file /home/mirte/shutdown_done exists + echo "correct shutdown" + rm /home/mirte/.shutdown +else + echo "incorrect shutdown" + touch /home/mirte/.shutdown_incorrect + # append date to shutdown_incorrect file + date >>/home/mirte/.shutdown_incorrect +fi +"$(dirname "$0")/mirte_master_check.sh" & diff --git a/services/mirte_expandoverlay.sh b/services/mirte_expandoverlay.sh new file mode 100755 index 0000000..39347dc --- /dev/null +++ b/services/mirte_expandoverlay.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# check if overlayfs is mounted +if grep -qs 'overlayroot' /proc/mounts; then + echo "overlayroot is on." +else + echo "overlayroot is off. Exiting." + exit 0 +fi + +# check if /home/mirte/.overlay_expanded file exists +if [ -f /home/mirte/.overlay_expanded ]; then + echo "Overlay is already expanded." + exit 0 +fi + +# get partition of /media/root-rw +DISK_AND_PART=$(realpath /dev/disk/by-label/mirte_root) # always this name + +# remove p1 from $DISK_AND_PART +DISK=/dev/$(lsblk -ndo pkname $DISK_AND_PART) + +# get number at end of $DISK_AND_PART +PART=$(echo $DISK_AND_PART | grep -o '[0-9]*$') +cat </tmp/batteryState & +ECHO_PID=$! +while ! systemctl list-jobs | grep -q -E 'shutdown.target.*start' && ! $STOP; do + OK=false + + # echo "topics" + percentage=$( + tail -2 /tmp/batteryState | head -1 + ) || true + echo $percentage + true >/tmp/batteryState + # percentage=$(echo "$percentage" | tail -1) + # echo $percentage + # echo $( echo $percentage | wc -c) + if [ "$(echo $percentage | wc -c)" -gt 1 ]; then + # echo "percentage" + percentage=$(echo "$percentage" | awk '{print $NF}') + # echo $percentage + if (($(echo "$percentage > 0.1" | bc -l))); then + OK=true + fi + fi + + if $OK; then + # echo "latest percentage: " + # echo $percentage + SECONDS=0 # update time since last ok + LAST_SECONDS=0 + WARN_LVL=0 + fi + # if there is a time jump and seconds is more than last_seconds+2, reset seconds + # time jumps happen when the time is updated (on connecting to wifi) + next_second=$((LAST_SECONDS + 20)) + if [ $SECONDS -gt $next_second ]; then + echo "time jump" + SECONDS=0 + LAST_SECONDS=0 + WARN_LVL=0 + fi + LAST_SECONDS=$SECONDS + + if [ $SECONDS -gt 300 ] && [ $WARN_LVL -eq 0 ]; then + wall "No ROS for longer than 5min" + WARN_LVL=1 + fi + if [ $SECONDS -gt 600 ] && [ $WARN_LVL -eq 1 ]; then + wall "No ROS for longer than 10min, shutting down in 5min" + WARN_LVL=2 + fi + if [ $SECONDS -gt 900 ] && [ $WARN_LVL -eq 2 ]; then + wall "shutting down" + WARN_LVL=3 + date >/home/mirte/.shutdown_power + sudo shutdown now + fi + sleep 10 +done + +kill $ECHO_PID || true +jobs -p +ros2 daemon stop || true # deamon is sometimes started by ros2 topic echo, otherwise the shutdown will get stuck and wait for 30+ seconds diff --git a/services/mirte_ros.sh b/services/mirte_ros.sh new file mode 100755 index 0000000..f625daa --- /dev/null +++ b/services/mirte_ros.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# by default use minimal launch file, but allow to override to minimal_master +LAUNCH_FILE="${1:-minimal}" + +# If the robot user wants to add their own config: +source /home/mirte/.mirte_settings.sh +mkdir -p $ROS_LOG_DIR + +source /home/mirte/mirte_ws/install/setup.bash +# if zenoh is enabled, start the zenoh daemon +if [ "$RMW_IMPLEMENTATION" = "rmw_zenoh_cpp" ]; then + # kill ros deamon + sudo pkill -9 -f ^ros && ros2 daemon stop + ros2 run rmw_zenoh_cpp rmw_zenohd & +fi +if [ "$MIRTE_FASTDDS" = "true" ]; then + if [ -z "$(ss -l | grep -w "11811")" ]; then + echo "starting discovery server on port 11811" + # echo "Please run a discovery server on port 11811 with the following command:" + fastdds discovery --server-id 0 -p 11811 & + fi + ros2 daemon stop + ros2 daemon start +fi +ros2 launch mirte_bringup $LAUNCH_FILE.launch.py diff --git a/services/mirte_shutdown.sh b/services/mirte_shutdown.sh new file mode 100755 index 0000000..349e427 --- /dev/null +++ b/services/mirte_shutdown.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# set -xe +# Don't shutdown if only stopping the service + +if ! systemctl list-jobs | grep -q -E 'shutdown.target.*start'; then + echo "shutdown target not active" + exit +fi + +source /home/mirte/.bashrc +source /home/mirte/.mirte_settings +source /home/mirte/mirte_ws/install/setup.bash +touch /home/mirte/.shutdown +service=/io/oled/oled/set_text +if [ "$MIRTE_USE_MULTIROBOT" = "true" ]; then + mirte_space=$(cat /etc/hostname | tr '[:upper:]' '[:lower:]' | tr '-' '_') + service="/$mirte_space$service" +fi + +if [ "$(ros2 service list | grep "$service$")" ]; then + ros2 service call "$service" mirte_msgs/srv/SetOLEDText "{ text: 'Shutting down...'}" +fi diff --git a/services/mirte_web_interface.sh b/services/mirte_web_interface.sh new file mode 100755 index 0000000..06487b0 --- /dev/null +++ b/services/mirte_web_interface.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -xe +source /home/mirte/.mirte_settings.sh + +source /home/mirte/mirte_ws/install/setup.bash + +python3 -m mirte_robot.linetrace & + +cd /usr/local/src/mirte/mirte-web-interface/rosboard +./run & + +cd /usr/local/src/mirte/mirte-web-interface/ +source ./node_env/bin/activate +cd nodejs-backend +npm run backend diff --git a/services/readme.md b/services/readme.md new file mode 100644 index 0000000..59d4e32 --- /dev/null +++ b/services/readme.md @@ -0,0 +1,28 @@ +# Services + +List of services: +- mirte-ap: + - start wifi hotspot at boot +- mirte-jupyter: + - starts jupyter, not on by default +- mirte-ros + - starts ROS using mirte_ros.sh + - this tries to guess the IP of the robot by using hostname -I and uses this as ROS_IP + - +- mirte-shutdown + - Only for Mirte-master + - Will run mirte_boot.sh and mirte_shutdown.sh + - boot: + - check if last shutdown was correct (does ~/.shutdown exist) and write failures to ~/.shutdown_incorrect + - starts mirte_master_check.sh + - checks for /mirte/power/power_watcher::percentage + - if not exists for too long(15min): shutdown + - shutdown: + - writes the ~/.shutdown file that is checked at boot + - Prints "Shutting down....." to the oled screen by calling the rosservice +- mirte-usb-switch: + - starts the usb-switch to turn on the orbbec camera +- mirte-web-interface: + - starts the web interface +- mirte-wifi-watchdog: + - TODO: not sure \ No newline at end of file diff --git a/sites/401.html b/sites/401.html new file mode 100644 index 0000000..f13f9a8 --- /dev/null +++ b/sites/401.html @@ -0,0 +1,18 @@ + + + +Welcome to Mirte! + + + +

Welcome to Mirte!

+

If you see this page, you are accessing this page from different network than one provided by your Mirte robot. Log in with username mirte and the mirte password.

+ + + diff --git a/tools.sh b/tools.sh new file mode 100755 index 0000000..f78fa1d --- /dev/null +++ b/tools.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# some functions to help with the setup + +add_rc() { + lines=$1 + + grep -qxF "$lines" ~/.bashrc || echo "$lines" >>~/.bashrc + + # if we have a second argument, we use that as lines to add to zshrc, otherwise keep the same as bashrc + if [ -z "$2" ]; then + lines=$1 + else + lines=$2 + fi + grep -qxF "$lines" ~/.zshrc || echo "$lines" >>~/.zshrc +} + +add_mirte_settings() { + lines=$1 + echo "$lines" >>~/.mirte_settings.sh +} diff --git a/usb_ethernet.sh b/usb_ethernet.sh index b06c95a..989b2f8 100755 --- a/usb_ethernet.sh +++ b/usb_ethernet.sh @@ -3,7 +3,7 @@ # This script creates USB gadgets usign ConfigFS for both Linux/MacOS and Windows # The Linux and MacOS version will connect to usb0, whil Windows will connect # to usb1. Both networks then are shown on the host. -MIRTE_SRC_DIR=/usr/local/src/mirte +MIRTE_SRC_DIR=${MIRTE_SRC_DIR:-/usr/local/src/mirte} sudo killall -9 dnsmasq sudo $MIRTE_SRC_DIR/mirte-install-scripts/ev3-usb.sh down "$(ls /sys/class/udc | tail -n1)" || true @@ -40,6 +40,9 @@ for USB_PATH in /sys/class/net/usb*; do if [[ $USB == "usb1" ]]; then continue fi + if [[ $USB != usb* ]]; then + continue + fi setup_network_usb $USB $SUBNET ((SUBNET += 1)) done diff --git a/wifi-watchdog.sh b/wifi-watchdog.sh index 1777f88..56da689 100755 --- a/wifi-watchdog.sh +++ b/wifi-watchdog.sh @@ -1,16 +1,11 @@ #!/bin/sh -#echo "begin watchdog" > /home/mirte/test.txt - -# echo "wifi watchdog service restarted" > /dev/kmsg - -# sub="xradio" - dmesg --follow | while read -r line; do - if echo "$line" | grep -q "xradio WSM-ERR: CMD timeout!"; then + # OrangePi Zero 1: xradio WSM-ERR: CMD timeout! + # OrangePi Zero 2: WCN_ERR: dumpmem_rx_callback open + if echo "$line" | grep -qE "xradio WSM-ERR: CMD timeout!|WCN_ERR: dumpmem_rx_callback open"; then echo "CRASH! REBOOT!" >/dev/kmsg - echo "$line" >>./test.txt - # reboot -f now + echo "$line" >>/home/mirte/wifi-watchdog.err echo b >/proc/sysrq-trigger fi done