diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index bd5e41b..b304e0f 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -34,6 +34,7 @@ jobs:
uses: pguyot/arm-runner-action@v2
with:
#bind_mount_repository: true
+ base_image: raspios_lite_arm64:latest
copy_artifact_path: dist
commands: |
apt install -y python3-pip
@@ -76,7 +77,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: |
- ${{ steps.file_names.outputs.file_path }}
+ ${{ steps.file_names.outputs.versioned_release_file_path }}
tag_name: ${{ github.event.inputs.release_version }}
name: Release ${{ github.event.inputs.release_version }}
draft: true
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 7944b09..cdfd79e 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/rPyCamera.iml b/.idea/rPyCamera.iml
index 74d515a..dbe758c 100644
--- a/.idea/rPyCamera.iml
+++ b/.idea/rPyCamera.iml
@@ -4,7 +4,7 @@
-
+
\ No newline at end of file
diff --git a/main.py b/main.py
deleted file mode 100644
index 2aad362..0000000
--- a/main.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# This is a sample Python script.
-import os
-import pathlib
-import shutil
-import uuid
-from string import Template
-
-
-# Press Shift+F10 to execute it or replace it with your code.
-# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
-
-def ask_confirmation(question: str):
- while True:
- answer = input(question + '\n [y/n] > ')
- if answer.lower() in ["y", "yes"]:
- return True
- elif answer.lower() in ["n", "no"]:
- return False
- else:
- print("Please respond with 'y'/'yes' or 'n'/'no'")
-
-
-def ask_orientation() -> int:
- while True:
- answer = input(
- 'Where on the image is the edge/side that should be normally on the bottom?\n [Left / Right / Up / Down] > ')
- if answer.lower() in ["l", "left"]:
- return 270 # -90
- elif answer.lower() in ["r", "right"]:
- return 90
- elif answer.lower() in ["u", "up"]:
- return 180
- elif answer.lower() in ["d", "down"]:
- return 180
- else:
- print("Please respond with 'l'/'left' or 'r'/'right' or 'u'/'up'/ or 'd'/'down'")
-
-
-def send_image(rotation: int) -> None:
- print("Image sent")
-
-
-def create_autorun_script(api_key: str, camera_fingerprint: str, camera_rotation: int) -> str:
- template_values = {
- 'rotation': camera_rotation,
- 'fingerprint': camera_fingerprint,
- 'prusa_api_key': api_key
- }
-
- with open('run-camera-capture--template.sh', 'r') as template_file:
- template = Template(template_file.read())
- file_content_result = template.substitute(template_values)
- script_file = open("run-camera-capture.sh", "w")
- script_file.write(file_content_result)
- script_file.close()
- template_file.close()
-
- return os.path.realpath(script_file.name)
-
-
-def check_camera_orientation(rotation: int) -> int:
- while not ask_confirmation("Is the image orientation as expected? Bottom side is on the bottom?"):
- rotation = ask_orientation()
- send_image(rotation)
- return rotation
-
-
-if __name__ == '__main__':
- token = input('Please insert your "Prusa Connect API Key" \n > ')
- fingerprint = uuid.uuid4().hex
- cam_rotation = 0
-
- print("I will try to create the connection now by capturing an image from the RPi camera and send it to the Prusa "
- "Connect. Make sure the printer is turned on and ONLINE in the Prusa Connect.")
- if ask_confirmation("Are you ready to test the connection?"):
- try:
- send_image(cam_rotation)
- if ask_confirmation("Is the image visible in Prusa Connect? (If the image is incorrectly rotated, "
- "don't worry we will fix that later)"):
- print("Congratulations. The connection works as expected.")
- cam_rotation = check_camera_orientation(cam_rotation)
- autorun_script_path = create_autorun_script(token, fingerprint, cam_rotation)
-
- if ask_confirmation(
- "Image is correctly rotated and we are ready to create autorun script. This script will be "
- "registered to run on start of the RPi OS and will capture image every 10 seconds that will "
- "be sent to the Prusa Connect. Do you want to continue?"
- ):
- print("Creating autorun script...")
- shell_final_path = "/usr/local/bin/prusa-connect-camera.sh"
- shutil.copy(autorun_script_path, shell_final_path)
- service_file_name = 'prusa-connect-camera.service'
- with open('prusa-connect-camera--template.service', 'r') as service_template_file:
- service_template = Template(service_template_file.read())
- service_file_content = service_template.substitute({'shell_script_path': shell_final_path})
- service_file = open(service_file_name, "w")
- service_file.write(service_file_content)
- service_file.close()
-
- service_template_file.close()
- service_file_src_path = os.path.realpath(service_file.name)
- service_file_dst_path = f'/etc/systemd/system/{service_file_name}';
- shutil.copy(service_file_src_path, service_file_dst_path)
-
- print("Starting the service daemon...")
- exec(f'sudo systemctl start {service_file_name}')
- exec(f'sudo systemctl enable {service_file_name}')
- else:
- print(
- "There is nothing much I can help with here as my main purpose is to install the script "
- "mentioned in the question above. Please re-run the installer if you change your mind.\n\n"
- "I have created a shell script that you can run on your own. The script will capture a "
- "snapshot image from the camera and send to the Prusa Connect. You can find it in: "
- f"{autorun_script_path}")
- exit(0)
- else:
- print("This is very unexpected. It might be due to various issues. Please make sure the 'Prusa "
- "Connect API Key' you provided is correct, the camera is connected to Raspberry Pi. If all is "
- "correct I do recommend to try create an image capture from the Camera via a Shell command "
- "`rpicam-still`.")
- except Exception as e:
- print(e)
-
- else:
- print("Creating a first image capture from the script is essential for the setup. If you change your mind, "
- "please run the script again. Exiting for now...")
- exit(0)
diff --git a/src/build.spec b/src/build.spec
new file mode 100644
index 0000000..b7e7398
--- /dev/null
+++ b/src/build.spec
@@ -0,0 +1,37 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+from PyInstaller.utils.hooks import collect_data_files
+
+a = Analysis(
+ ['main.py'],
+ pathex=[],
+ binaries=[],
+ datas=[("templates/*", "templates/")],
+ hiddenimports=[],
+ hookspath=[],
+ hooksconfig=[],
+ runtime_hooks=[],
+ excludes=[],
+ noarchive=False,
+)
+pyz = PYZ(a.pure)
+
+exe = EXE(
+ pyz,
+ a.scripts,
+ a.binaries,
+ a.datas,
+ [],
+ name='install-camera',
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ upx_exclude=[],
+ runtime_tmpdir=None,
+ console=True,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ codesign_identity=None,
+ entitlements_file=None,
+)
\ No newline at end of file
diff --git a/src/main.py b/src/main.py
new file mode 100644
index 0000000..f2e9540
--- /dev/null
+++ b/src/main.py
@@ -0,0 +1,351 @@
+from sys import exit
+import os
+import pathlib
+import re
+import shutil
+import subprocess
+import sys
+import json
+import uuid
+from string import Template
+
+def print_success() -> None:
+ print('')
+ print('')
+ print(' @@@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@@@ @@@@@@@@@ ')
+ print(' @@@@@@@@@@@@@ @@ @@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@@@ @@@ @ @@@@@ ')
+ print(' @@@ @@ @@@@ ')
+ print(' @ @@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@ ')
+ print(' @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@ ')
+ print(' @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@ ')
+ print(' @@@@@@@@@@ @@@@@@@@@@@ @@ ')
+ print(' @@@@@@@@@ @@@@@@@@@@ @@ ')
+ print(' @@@@@@@@@ @@@@@@@@@ @ ')
+ print(' @@@@@@@@ @@@@@@@@ @ ')
+ print(' @@@@@@@@ @@@@@@@@ ')
+ print(' @@@@@@@@ @@@@@@@@ ')
+ print(' @@@@@ @@@@@@@@ ')
+ print(' @@@@@ @@@@@ @@ @@@@@ ')
+ print(' @@@@@@@ @@@@@ @@ @@@@ ')
+ print(' @@ @@ @@ @@ @@ ')
+ print(' @@ @ @@ @@@@@@@ @@@@@@@@@@@ @ @@@ ')
+ print(' @@ @ @@@@ @ @ @@ @@@@@@@@@ ')
+ print(' @ @ @ @ @@ @ ')
+ print(' @@ @ @@ @ @ @ @@ ')
+ print(' @@ @@ @@ @@ @ @@ ')
+ print(' @@ @@@@@ @@ @ @ ')
+ print(' @ @@@ @@ @ ')
+ print(' @@ @@@@ @@ ')
+ print(' @@ @@@ @ ')
+ print(' @@ @ ')
+ print(' @ @@ @@ ')
+ print(' @ @@@@@@ @@ ')
+ print(' @@ @@@@@@@@@@@@ @@@ @@ ')
+ print(' @@ @@@@@@@@@@@@@ @@ ')
+ print(' @@@ @@ ')
+ print(' @@@@ @@ ')
+ print(' @@@@@ @@@@ ')
+ print(' @@@@@@ @@@@@ ')
+ print(' @@@@@@@ @@@@@ ')
+ print(' @@@@@@@@@@ @@@@@@@@ ')
+ print(' @@@@@@@@@@@@@@@@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@@@@@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@@@@@@@@ ')
+ print(' @@@@@@@@ ')
+ print(' ')
+ print(' ')
+ print('Happy Printing')
+
+
+def ask_confirmation(question: str):
+ while True:
+ answer = input(question + '\n [y/n] > ')
+ if answer.lower() in ["y", "yes"]:
+ return True
+ elif answer.lower() in ["n", "no"]:
+ return False
+ else:
+ print("Please respond with 'y'/'yes' or 'n'/'no'")
+
+
+def ask_orientation() -> int:
+ while True:
+ answer = input(
+ 'Where on the image (The first image you seen, not the most recent) is the edge/side that should be '
+ 'normally on the bottom?\n [Left / Right / Up / Down] > ')
+ if answer.lower() in ["l", "left"]:
+ return 270 # -90
+ elif answer.lower() in ["r", "right"]:
+ return 90
+ elif answer.lower() in ["u", "up"]:
+ return 180
+ elif answer.lower() in ["d", "down"]:
+ return 0
+ else:
+ print("Please respond with 'l'/'left' or 'r'/'right' or 'u'/'up'/ or 'd'/'down'")
+
+
+def send_image(cam_rotation: int, cam_fingerprint: str, cam_token: str) -> None:
+ # Make sure the output is hidden as that does not work well add some verbose option to show it
+ subprocess.run(["rpicam-still", "-v", "0", "--immediate", "--width", "2274", "--height", "1280", "-q", "80", "-o",
+ "cam_snapshot.jpg"], capture_output=True)
+ # Validate the image has been created if not there is an issue with the rpicam-still and we should print the
+ # error output
+ if cam_rotation != 0:
+ subprocess.run(
+ ["ffmpeg", "-y", "-i", "cam_snapshot.jpg", "-vf", f'rotate={cam_rotation}*PI/180', "cam_snapshot.jpg"],
+ capture_output=True)
+
+ curl_send_image_output = subprocess.run(['curl', '-X', 'PUT', 'https://connect.prusa3d.com/c/snapshot',
+ '-H', 'accept: */*',
+ '-H', 'content-type: image/jpg',
+ '-H', f'fingerprint: {cam_fingerprint}',
+ '-H', f'token: {cam_token}',
+ '--data-binary', '@cam_snapshot.jpg',
+ '--no-progress-meter',
+ '--compressed'], capture_output=True)
+ if curl_send_image_output.returncode != 0:
+ print("")
+ print(f'{curl_send_image_output.stdout.decode(sys.getfilesystemencoding())}')
+ print(f'{curl_send_image_output.stderr.decode(sys.getfilesystemencoding())}')
+ print("")
+ print("Image wasn't sent, check the error above.")
+ exit(512)
+ raw_response = curl_send_image_output.stdout.decode(sys.getfilesystemencoding())
+ data = json.loads(raw_response)
+ if 'status_code' in data:
+ status_code = data['status_code']
+ if 200 <= status_code < 300:
+ print("Image sent successfully...")
+ return
+ else:
+ print(data)
+ print('')
+ print('There was an error sending the image. See details above.')
+
+
+def create_autorun_script(camera_token: str, camera_fingerprint: str, camera_rotation: int) -> str:
+ template_values = {
+ 'rotation': camera_rotation,
+ 'fingerprint': camera_fingerprint,
+ 'token': camera_token
+ }
+
+ with open(get_filepath('run-camera-capture--template.sh'), 'r') as template_file:
+ template = Template(template_file.read())
+ file_content_result = template.substitute(template_values)
+ if camera_rotation == 0:
+ file_content_result = re.sub(r'^(.*ffmpeg.*)$', r'#\1', file_content_result)
+
+ script_file = open("run-camera-capture.sh", "w")
+ script_file.write(file_content_result)
+ script_file.close()
+ template_file.close()
+
+ return os.path.realpath(script_file.name)
+
+
+def check_camera_orientation(rotate: int, cam_fingerprint: str, cam_token: str) -> int:
+ ffmpeg_installed = None
+ while not ask_confirmation("Is the image orientation as expected? Bottom side is on the bottom?"):
+ if ffmpeg_installed is None:
+ ffmpeg_installed = validate_ffmpeg()
+ if not ffmpeg_installed:
+ return 0
+ rotate = ask_orientation()
+ send_image(rotate, cam_fingerprint, cam_token)
+ return rotate
+
+
+def validate_ffmpeg() -> bool:
+ try:
+ ffmpeg_output = subprocess.run(["ffmpeg", "-version"], capture_output=True)
+ except Exception:
+ print("ffmpeg library is not installed. Software rotation of images won't be supported. Please rotate your "
+ "camera in a correct direction manually by rotating the hardware.")
+ if ask_confirmation("Or you you can install the ffmpeg library? Do you want to continue and install it now?"):
+ ffmpeg_install_output = subprocess.run(['sudo', 'apt', 'install', '-y', 'ffmpeg'], capture_output=False)
+ if ffmpeg_install_output.returncode == 0:
+ print("ffmpeg library is installed correctly.")
+ return True
+ else:
+ print("ffmpeg library is not installed. Try to install it manually by running `sudo apt install -y"
+ "ffmpeg`")
+ exit(501)
+ else:
+ if not ask_confirmation(
+ "You can also install ffmpeg by running this command in your terminal: `sudo apt install -y "
+ "ffmpeg` if you still prefer to rotate your image via software option. Do you want to continue "
+ "without the camera rotation."):
+ exit(510)
+ return False
+ return True
+
+
+def validate_camera_installed() -> bool:
+ try:
+ rpicam_version_output = subprocess.run(["rpicam-still", "--version"], capture_output=True)
+ except Exception:
+ if ask_confirmation("Camera libraries for Raspberry is not installed. Do you want to install it now?"):
+ rpicam_install_output = subprocess.run(['sudo', 'apt', 'install', '-y', 'rpicam-apps'],
+ capture_output=False)
+ if rpicam_install_output.returncode == 0:
+ print("rpicam-apps library is installed correctly.")
+ return True
+ else:
+ print("rpicam-apps library is not installed. Try to install it manually by running `sudo apt install -y"
+ "rpicam-apps`")
+ exit(502)
+ print("rpicam-still program from rpicam-apps library is required. Try to install it manually by running `sudo "
+ "apt install rpicam-apps -y`")
+ exit(503)
+ return True
+
+
+def validate_camera() -> bool:
+ validate_camera_installed()
+ if ask_confirmation("Let's validate the camera works. Camera feed will be visible on a screen in new window for "
+ "3s. Are you ready to continue?"):
+ rpicam_hello_output = subprocess.run(["rpicam-hello", "--timeout", "3000"], capture_output=True)
+ if rpicam_hello_output.returncode != 0:
+ print("")
+ print(f'{rpicam_hello_output.stdout.decode(sys.getfilesystemencoding())}')
+ print(f'{rpicam_hello_output.stderr.decode(sys.getfilesystemencoding())}')
+ print("")
+ print(
+ "Cannot connect and show the camera image feed. Please make sure the camera hardware is connected. Try "
+ "to restart the board and re-run the script please. The error details are printed above.")
+ exit(511)
+ else:
+ if ask_confirmation("Have you seen the camera feed in the window that has been open for 3s?"):
+ return True
+ else:
+ print("This is unexpected. Please validate the camera hardware is still connected, try to reboot the "
+ "Raspberry Pi and re-run the script.")
+ exit(504)
+ else:
+ print("This step is required. Please re-run the script once ready.")
+ exit(505)
+
+
+def validate_curl() -> bool:
+ try:
+ curl_version_output = subprocess.run(["curl", "--version"], capture_output=True)
+ except Exception:
+ if ask_confirmation("Required library `curl` not found. Do you want to continue and install it now?"):
+ curl_install_output = subprocess.run(['sudo', 'apt', 'install', '-y', 'curl'], capture_output=False)
+ if curl_install_output.returncode == 0:
+ print("curl library is installed correctly.")
+ return True
+ else:
+ print("curl library is not installed. Try to install it manually by running `sudo apt install "
+ "curl -y`")
+ exit(506)
+ print("curl library is required. Try to install it manually by running `sudo apt install curl -y`")
+ exit(507)
+ return True
+
+
+def validate_sudo() -> None:
+ if os.getuid() != 0:
+ print(f"This process needs to be run as root. Please re-run as `sudo {sys.executable} {' '.join(sys.argv)}`")
+ exit(510)
+
+
+def validate_requirements() -> None:
+ validate_sudo()
+ validate_curl()
+ validate_camera()
+
+
+def get_filepath(name: str) -> pathlib.PurePath:
+ return pathlib.Path(__file__).resolve().parent.joinpath("templates").joinpath(name)
+
+
+if __name__ == '__main__':
+ service_file_name = 'prusa-connect-camera.service'
+ validate_requirements()
+ fingerprint = f'{uuid.uuid3(uuid.NAMESPACE_URL, hex(uuid.getnode()))}'
+ rotation = 0
+ token = input('Please insert your "Prusa Camera Token" \n > ')
+
+ print("")
+ print("Using:")
+ print(f" - Camera Fingerprint: '{fingerprint}'")
+ print(f" - Camera Token: '{token}'")
+ print("")
+
+ print("I will try to create the connection now by capturing an image from the RPi camera and send it to the Prusa "
+ "Connect. Make sure the printer is turned on and ONLINE in the Prusa Connect.")
+ if ask_confirmation(
+ "Are you ready to test the connection? (Note: This will stop the automatic screenshot service if there is "
+ "any already from previous installations.)"):
+ try:
+ subprocess.run(['sudo', 'systemctl', 'stop', service_file_name])
+ send_image(rotation, fingerprint, token)
+ if ask_confirmation(
+ "Is the image visible in Prusa Connect (refresh the page please)? If the image is incorrectly "
+ "rotated, don't worry we will fix that later."
+ ):
+ print("Congratulations. The connection works as expected.")
+ rotation = check_camera_orientation(rotation, fingerprint, token)
+ autorun_script_path = create_autorun_script(token, fingerprint, rotation)
+
+ if ask_confirmation(
+ "Image is correctly rotated and we are ready to create autorun script. This script will be "
+ "registered to run on start of the RPi OS and will capture image every 10 seconds that will "
+ "be sent to the Prusa Connect. Do you want to continue?"
+ ):
+ print("Creating autorun script...")
+ shell_final_path = "/usr/local/bin/prusa-connect-camera.sh"
+ shutil.copy(autorun_script_path, shell_final_path)
+ service_file_name = 'prusa-connect-camera.service'
+ with open(get_filepath('prusa-connect-camera--template.service'), 'r') as service_template_file:
+ service_template = Template(service_template_file.read())
+ service_file_content = service_template.substitute({'shell_script_path': shell_final_path})
+ service_file = open(service_file_name, "w")
+ service_file.write(service_file_content)
+ service_file.close()
+
+ service_template_file.close()
+ service_file_src_path = os.path.realpath(service_file.name)
+ service_file_dst_path = f'/etc/systemd/system/{service_file_name}'
+ shutil.copy(service_file_src_path, service_file_dst_path)
+
+ print("Starting the service daemon...")
+ subprocess.run(['sudo', 'chmod', '+x', shell_final_path])
+ subprocess.run(['sudo', 'systemctl', 'daemon-reload'])
+ subprocess.run(['sudo', 'systemctl', 'start', service_file_name])
+ subprocess.run(['sudo', 'systemctl', 'enable', service_file_name])
+ subprocess.run(['sudo', 'systemctl', 'status', service_file_name, '-n', '1'])
+ print('')
+ print('Congratulations. You have finished the setup.')
+ print(
+ 'If the service above is marked as Enabled, Active and Running, you should see the image to '
+ 'be updated every 10 sec. in the Prusa Connect.')
+ print('')
+ input('Press any key to exit the setup...')
+ print_success()
+ else:
+ print(
+ "There is nothing much I can help with here as my main purpose is to install the script "
+ "mentioned in the question above. Please re-run the installer if you change your mind.\n\n"
+ "I have created a shell script that you can run on your own. The script will capture a "
+ "snapshot image from the camera and send to the Prusa Connect. You can find it in: "
+ f"{autorun_script_path}")
+ exit(508)
+ else:
+ print("This is very unexpected. It might be due to various issues. Please make sure the 'Prusa "
+ "Connect API Key' you provided is correct, the camera is connected to Raspberry Pi. If all is "
+ "correct I do recommend to try create an image capture from the Camera via a Shell command "
+ "`rpicam-still`.")
+ except Exception as e:
+ print(e)
+
+ else:
+ print("Creating a first image capture from the script is essential for the setup. If you change your mind, "
+ "please run the script again. Exiting for now...")
+ exit(509)
diff --git a/prusa-connect-camera--template.service b/src/templates/prusa-connect-camera--template.service
similarity index 100%
rename from prusa-connect-camera--template.service
rename to src/templates/prusa-connect-camera--template.service
diff --git a/run-camera-capture--template.sh b/src/templates/run-camera-capture--template.sh
similarity index 59%
rename from run-camera-capture--template.sh
rename to src/templates/run-camera-capture--template.sh
index e5a9161..45d28ac 100644
--- a/run-camera-capture--template.sh
+++ b/src/templates/run-camera-capture--template.sh
@@ -1,22 +1,23 @@
#!/bin/bash
while true; do
- libcamera-still -v 0 --immediate --width 2274 --height 1280 --rotate $rotation -q 80 -o cam_snapshot.jpg
+ rpicam-still -v 0 --immediate --width 2274 --height 1280 -q 80 -o cam_snapshot.jpg
+ ffmpeg -y -i cam_snapshot.jpg -vf "rotate=$rotation*PI/180" cam_snapshot.jpg
if [ $$? -eq 0 ]; then
curl -X PUT "https://connect.prusa3d.com/c/snapshot" \
-H "accept: */*" \
-H "content-type: image/jpg" \
-H "fingerprint: $fingerprint" \
- -H "token: $prusa_api_key" \
+ -H "token: $token" \
--data-binary "@cam_snapshot.jpg" \
--no-progress-meter \
--compressed
sleep 10
else
- echo "libcamera-still returned an error, retrying after 60s..."
+ echo "rpicam-still returned an error, retrying after 60s..."
sleep 60
fi
-done
\ No newline at end of file
+done