diff --git a/Software/LMSourceCode/ImageProcessing/CameraTools/v4l2_trigger b/Software/LMSourceCode/ImageProcessing/CameraTools/v4l2_trigger new file mode 100755 index 00000000..3289c182 --- /dev/null +++ b/Software/LMSourceCode/ImageProcessing/CameraTools/v4l2_trigger @@ -0,0 +1,20 @@ +#!/bin/bash +# +# PiTrac External Trigger Utility for Mira220 (arducam-pivariety) +# +# Usage: v4l2_trigger +# subdev_index: v4l-subdev index (e.g., 2 for /dev/v4l-subdev2 on Pi 5) +# mode: 0 = disable external trigger, 1 = enable external trigger +# +# Example: v4l2_trigger 2 1 # Enable external trigger on /dev/v4l-subdev2 +# +# This uses the Arducam pivariety driver's v4l2 control 'trigger_mode': +# - trigger_mode=0: normal streaming (internal trigger) +# - trigger_mode=1: external trigger via FSIN pin, camera waits for signal +# + +SUBDEV="${1:-2}" +MODE="${2:-1}" + +v4l2-ctl -d "/dev/v4l-subdev${SUBDEV}" -c "trigger_mode=${MODE}" +exit $? diff --git a/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp b/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp index dbf930ca..993ca5d2 100644 --- a/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp +++ b/Software/LMSourceCode/ImageProcessing/camera_hardware.cpp @@ -88,6 +88,7 @@ namespace golf_sim { { "3", CameraModel::PiHQ }, { "4", CameraModel::PiGS }, { "5", CameraModel::InnoMakerIMX296GS_Mono }, + { "6", CameraModel::Mira220_Mono }, { "100", CameraModel::kCameraUnknown }, }; if (camera_table.count(model_enum_value_string) == 0) @@ -133,7 +134,7 @@ namespace golf_sim { bool CameraHardware::camera_is_mono() const { - return (camera_model_ == CameraModel::InnoMakerIMX296GS_Mono); + return (camera_model_ == CameraModel::InnoMakerIMX296GS_Mono || camera_model_ == CameraModel::Mira220_Mono); } @@ -236,17 +237,17 @@ namespace golf_sim { } // This section deals with the common characteristics of some of the cameras - if (model == PiGS || + if (model == PiGS || model == InnoMakerIMX296GS_Mono) { - GS_LOG_TRACE_MSG(trace, "Initializing with a PiGS or InnoMakerIMX296GS_Mono camera." ); + GS_LOG_TRACE_MSG(trace, "Initializing with a PiGS or InnoMakerIMX296GS_Mono camera."); // Sensor pixel width is 3.45uM square? No - 6.33mm diagonal. It appears that // the actual width is the full resolution (1456) * 3.4uM = 4.95mm, // Not simply the diagonal sensor width sensor_width_ = (float)5.077365371; // 4.45; // (1456.0 * 3.4) / 1000; // = 4.95; // In mm 6.3 / sqrt(2.0); // TBD - Confirm math from diagonal measurement sensor_height_ = (float)3.789078635; // 4.45; //(1088.0 * 3.4) / 1000; // In mm 6.3 / sqrt(2.0); - + if (resolution_x_override_ > 0 && resolution_y_override_ > 0) { resolution_x_ = resolution_x_override_; resolution_y_ = resolution_y_override_; @@ -263,6 +264,43 @@ namespace golf_sim { video_resolution_y_ = 1080; GS_LOG_TRACE_MSG(trace, "Video resolution (x,y) is: " + std::to_string(video_resolution_x_) + "/" + std::to_string(video_resolution_y_) + "."); + } + + if (model == Mira220_Mono) { + + // See https://look.ams-osram.com/m/7591efdbe4af32dc/original/Mira220-1-2-7-2-2-MP-NIR-enhanced-global-shutter-image-sensor.pdf for details + + GS_LOG_TRACE_MSG(trace, "Initializing with a Mira220_Mono - based camera."); + // Sensor pixel width is 3.45uM square? No - 6.33mm diagonal. It appears that + // the actual width is the full resolution (1456) * 3.4uM = 4.95mm, + // Not simply the diagonal sensor width + + sensor_width_ = (float)4.464; // 2.79um pixel size * 1600 + sensor_height_ = (float)3.906; // 2.79um pixel size * 1400 + + if (resolution_x_override_ > 0 && resolution_y_override_ > 0) { + resolution_x_ = resolution_x_override_; + resolution_y_ = resolution_y_override_; + } + else { + // Defaults + resolution_x_ = 1600; + resolution_y_ = 1400; + } + + // We ahve not tested . + video_resolution_x_ = resolution_x_; + video_resolution_y_ = resolution_y_; + + GS_LOG_TRACE_MSG(trace, "Video resolution (x,y) is: " + std::to_string(video_resolution_x_) + "/" + std::to_string(video_resolution_y_) + "."); + } + + + // Ball radius parameters are common to most all of our potential cameras + if (model == PiGS || + model == InnoMakerIMX296GS_Mono || + model == Mira220_Mono) { + // Attempt to get the expected ball radius from the .json file std::string ball_radius_pixels_at_40cm_name = "kExpectedBallRadiusPixelsAt40cmCamera" + std::string(camera_number == GsCameraNumber::kGsCamera1 ? "1" : "2"); diff --git a/Software/LMSourceCode/ImageProcessing/camera_hardware.h b/Software/LMSourceCode/ImageProcessing/camera_hardware.h index 4b9fe9b7..e07aba42 100644 --- a/Software/LMSourceCode/ImageProcessing/camera_hardware.h +++ b/Software/LMSourceCode/ImageProcessing/camera_hardware.h @@ -36,6 +36,7 @@ namespace golf_sim { PiHQ = 3, PiGS = 4, InnoMakerIMX296GS_Mono = 5, + Mira220_Mono = 6, kCameraUnknown = 100 }; diff --git a/Software/LMSourceCode/ImageProcessing/core/rpicam_app.cpp b/Software/LMSourceCode/ImageProcessing/core/rpicam_app.cpp index 714041ed..252a38a8 100644 --- a/Software/LMSourceCode/ImageProcessing/core/rpicam_app.cpp +++ b/Software/LMSourceCode/ImageProcessing/core/rpicam_app.cpp @@ -59,35 +59,9 @@ static libcamera::PixelFormat mode_to_pixel_format(Mode const &mode) static void set_pipeline_configuration(Platform platform) { - GS_LOG_TRACE_MSG(trace, "Setting pipeline configuration file.."); - - // Respect any pre-existing value in the environment variable. - char const *existing_config = getenv("LIBCAMERA_RPI_CONFIG_FILE"); - if (existing_config && existing_config[0]) { - GS_LOG_TRACE_MSG(trace, "LIBCAMERA_RPI_CONFIG_FILE already set."); - return; - } - - - // JPMOD - Added PISP options - // Otherwise point it at whichever of these we find first (if any) for the given platform. - static const std::vector> config_files = { - { Platform::VC4, "/usr/local/share/libcamera/pipeline/rpi/vc4/rpi_apps.yaml" }, - { Platform::VC4, "/usr/share/libcamera/pipeline/rpi/vc4/rpi_apps.yaml" }, - { Platform::PISP, "/usr/local/share/libcamera/pipeline/rpi/pisp/rpi_apps.yaml" }, - { Platform::PISP, "/usr/share/libcamera/pipeline/rpi/pisp/rpi_apps.yaml" }, - }; - - for (auto &config_file : config_files) - { - struct stat info; - if (config_file.first == platform && stat(config_file.second.c_str(), &info) == 0) - { - GS_LOG_TRACE_MSG(trace, "LIBCAMERA_RPI_CONFIG_FILE = " + config_file.second); - setenv("LIBCAMERA_RPI_CONFIG_FILE", config_file.second.c_str(), 1); - break; - } - } + // Let libcamera auto-detect the pipeline config — the hardcoded + // rpi_apps.yaml causes a segfault with Mira220 (Arducam pivariety). + (void)platform; } RPiCamApp::RPiCamApp(std::unique_ptr opts) diff --git a/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp b/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp index 1a690587..5f61f8f4 100644 --- a/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp +++ b/Software/LMSourceCode/ImageProcessing/libcamera_interface.cpp @@ -982,7 +982,13 @@ bool SetLibcameraTuningFileEnvVariable(const GolfSimCamera& camera) { std::string tuning_file; - if (camera.camera_hardware_.camera_is_mono()) { + // Mira220 (Arducam pivariety) — no matching tuning file exists. + // Let libcamera auto-detect the best fallback. + if (camera.camera_hardware_.camera_model_ == CameraHardware::CameraModel::Mira220_Mono) { + unsetenv("LIBCAMERA_RPI_TUNING_FILE"); + return true; + } + else if (camera.camera_hardware_.camera_is_mono()) { // If this is a mono camera, than we must use the "mono" tuning file, regardless of whether // the camera is camera 1 or camera 2 @@ -1012,7 +1018,9 @@ bool SetLibcameraTuningFileEnvVariable(const GolfSimCamera& camera) { } } - setenv("LIBCAMERA_RPI_TUNING_FILE", tuning_file.c_str(), 1); + if (!tuning_file.empty()) { + setenv("LIBCAMERA_RPI_TUNING_FILE", tuning_file.c_str(), 1); + } return true; } diff --git a/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp b/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp index 46b3bb25..26839656 100644 --- a/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp +++ b/Software/LMSourceCode/ImageProcessing/libcamera_jpeg.cpp @@ -82,6 +82,22 @@ void SetExternalTrigger(bool& flag) { return; } } + + if (!flag && camera_model == gs::CameraHardware::CameraModel::Mira220_Mono) { + + flag = true; + + std::string trigger_mode_command = "$PITRAC_ROOT/ImageProcessing/CameraTools/v4l2_trigger 2 1"; + + GS_LOG_TRACE_MSG(trace, "ball_flight_camera_event_loop - Camera 2 trigger_mode_command = " + trigger_mode_command); + int command_result = system(trigger_mode_command.c_str()); + + if (command_result != 0) { + GS_LOG_TRACE_MSG(trace, "system(trigger_mode_command) failed."); + return; + } + } + } // Run the triggered capture event loop on an already-opened camera. @@ -178,11 +194,9 @@ bool cam2_run_event_loop(LibcameraJpegApp& app, cv::Mat& returnImg, bool send_pr RPiCamApp::Msg msg = app.Wait(); if (msg.type == RPiCamApp::MsgType::Timeout) { - GS_LOG_MSG(error, "ERROR: Device timeout detected, attempting a restart!!!"); - app.StopCamera(); - uint flags = RPiCamApp::FLAG_STILL_RGB; - app.ConfigureViewfinder(flags); - app.StartCamera(); + // In external trigger mode, timeouts are expected while waiting + // for FSIN pulses. Just ignore and keep waiting for the trigger. + GS_LOG_TRACE_MSG(trace, "Device timeout (expected in external trigger mode) — continuing to wait."); continue; } diff --git a/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp b/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp index e340fde8..0d9f2aa4 100644 --- a/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp +++ b/Software/LMSourceCode/ImageProcessing/pulse_strobe.cpp @@ -585,7 +585,21 @@ namespace golf_sim { void PulseStrobe::SendOnOffPulse(long length_us) { #ifdef __unix__ // Ignore in Windows environment - if (kUsingActiveHighTriggerCamera) { + const CameraHardware::CameraModel camera_model = GolfSimCamera::kSystemSlot2CameraType; + + // The Mira220 external trigger (trigger_mode=1) requires a longer + // HIGH pulse on FSIN than the IMX296. The Connector Board inverts + // GPIO→XTR, so we swap the on/off durations: the normally-short + // LOW pulse becomes a long LOW (XTR HIGH for ~66ms at 15fps), + // which Mira220 detects reliably. + if (camera_model == CameraHardware::CameraModel::Mira220_Mono) { + int kOnTimeUs = (int)((1.0 / kPrimingPulseFPS) * 1000000. - length_us); + lgGpioWrite(lggpio_chip_handle_, kPulseTriggerOutputPin, kON); + usleep(length_us); + lgGpioWrite(lggpio_chip_handle_, kPulseTriggerOutputPin, kOFF); + usleep(kOnTimeUs); + lgGpioWrite(lggpio_chip_handle_, kPulseTriggerOutputPin, kON); + } else if (kUsingActiveHighTriggerCamera) { lgGpioWrite(lggpio_chip_handle_, kPulseTriggerOutputPin, kOFF); usleep(length_us); lgGpioWrite(lggpio_chip_handle_, kPulseTriggerOutputPin, kON); @@ -649,6 +663,8 @@ namespace golf_sim { } } + // NOTE - We're not yet sure if a Mira220-based sensor will need any priming pulses. However, it should probably work similar to the IMX296 + GS_LOG_TRACE_MSG(trace, "Sent " + std::to_string(kNumberPrimingPulses) + " initial priming pulses. About to pause for " + std::to_string(kPauseBeforeReadyForFinalPrimingPulseMs) + " milliSeconds before sending penultimate priming pulse."); diff --git a/Software/web-server/camera_detector.py b/Software/web-server/camera_detector.py index e93a85e1..c950c997 100644 --- a/Software/web-server/camera_detector.py +++ b/Software/web-server/camera_detector.py @@ -28,9 +28,10 @@ class CameraDetector: "imx477": "Pi HQ Camera", "imx296": "Global Shutter Camera", "imx708": "Pi Camera v3", + "arducam-pivariety": "Arducam Mira220 (Mono)", } - PITRAC_TYPES = {"ov5647": 1, "imx219": 2, "imx477": 3, "imx296": 4, "imx708": 0} + PITRAC_TYPES = {"ov5647": 1, "imx219": 2, "imx477": 3, "imx296": 4, "imx708": 0, "arducam-pivariety": 6} CAMERA_STATUS = { "ov5647": "DEPRECATED", @@ -38,15 +39,18 @@ class CameraDetector: "imx477": "DEPRECATED", "imx296": "SUPPORTED", "imx708": "UNSUPPORTED", + "arducam-pivariety": "EXPERIMENTAL", } PITRAC_TYPE_PI_GS = 4 PITRAC_TYPE_INNOMAKER = 5 + PITRAC_TYPE_MIRA220 = 6 DT_ROOT = "/sys/firmware/devicetree/base" DT_ROOT_ALT = "/proc/device-tree" INNOMAKER_TRIGGER = "/usr/lib/pitrac/ImageProcessing/CameraTools/imx296_trigger" + MIRA220_TRIGGER = "/usr/lib/pitrac/ImageProcessing/CameraTools/v4l2_trigger" def __init__(self): self.pi_model = self._detect_pi_model() @@ -194,7 +198,7 @@ def _parse_camera_info(self, output: str) -> List[Dict]: """Parse camera information from libcamera output""" cameras = [] - camera_pattern = r"^(\d+)\s*:\s*(\w+)\s*\[([^\]]+)\](?:\s*\(([^)]+)\))?" + camera_pattern = r"^(\d+)\s*:\s*([\w-]+)\s*\[([^\]]+)\](?:\s*\(([^)]+)\))?" for match in re.finditer(camera_pattern, output, re.MULTILINE): idx = int(match.group(1)) @@ -224,6 +228,9 @@ def _parse_camera_info(self, output: str) -> List[Dict]: else: pitrac_type = self.PITRAC_TYPE_PI_GS description = "IMX296 (assumed color)" + elif sensor == "arducam-pivariety": + pitrac_type = self.PITRAC_TYPE_MIRA220 + description = "Arducam Mira220 (Mono)" else: description = model_name @@ -536,6 +543,12 @@ def get_camera_types(self) -> List[Dict]: "description": "IMX296 Mono", "status": "supported", }, + { + "value": 6, + "label": "Arducam Mira220", + "description": "Mira220 Mono Global Shutter", + "status": "experimental", + }, ] def get_lens_types(self) -> List[Dict]: diff --git a/Software/web-server/configurations.json b/Software/web-server/configurations.json index 39d0de38..1a25f7c6 100644 --- a/Software/web-server/configurations.json +++ b/Software/web-server/configurations.json @@ -329,7 +329,8 @@ "2": "Pi Camera v2 - IMX219 sensor (DEPRECATED)", "3": "Pi HQ Camera - IMX477 sensor (DEPRECATED)", "4": "Pi Global Shutter - IMX296 Color", - "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)" + "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)", + "6": "Osram Mira220 - Mono sensor (EXPERIMENTAL)" }, "default": "5", "requiresRestart": true, @@ -391,7 +392,8 @@ "2": "Pi Camera v2 - IMX219 sensor (DEPRECATED)", "3": "Pi HQ Camera - IMX477 sensor (DEPRECATED)", "4": "Pi Global Shutter - IMX296 Color", - "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)" + "5": "InnoMaker IMX296 - Mono sensor (RECOMMENDED)", + "6": "Osram Mira220 - Mono sensor (EXPERIMENTAL)" }, "default": "5", "requiresRestart": true, diff --git a/Software/web-server/server.py b/Software/web-server/server.py index c20b89fd..2fe4fe89 100644 --- a/Software/web-server/server.py +++ b/Software/web-server/server.py @@ -38,7 +38,7 @@ # Locked to defeat AE/AWB/tuning drift across calibration frames (sub-pixel # corner bias). Tuning file matches production rcPi5GS.sh; shutter is shorter # than production (11 vs 20 ms) to limit hand-tremor blur. -RPICAM_TUNING_FILE = "/usr/share/libcamera/ipa/rpi/pisp/imx296_noir.json" +RPICAM_TUNING_FILE = "/usr/share/libcamera/ipa/rpi/pisp/imx296_noir.json" # NOTE - Mira220 will need a different tuning file. For now, folks can just put the Mira220 file in this file RPICAM_CAL_SHUTTER_US = 11000 RPICAM_CAL_GAIN = 1.3 diff --git a/detect_pi_cameras.sh b/detect_pi_cameras.sh index f2c74f12..3fccd8a4 100755 --- a/detect_pi_cameras.sh +++ b/detect_pi_cameras.sh @@ -43,6 +43,7 @@ declare -A CAMERA_MODELS=( ["imx477"]="Pi HQ Camera" ["imx296"]="Global Shutter Camera" ["imx708"]="Pi Camera v3" + ["arducam-pivariety"]="Arducam Mira220 (Mono)" ) # PiTrac type mappings (from camera_hardware.h) @@ -52,6 +53,7 @@ declare -A PITRAC_TYPES=( ["imx477"]=3 # Deprecated - not recommended ["imx296"]=4 # Default for IMX296 (Pi GS) ["imx708"]=0 # Unsupported + ["arducam-pivariety"]=6 # Arducam Mira220 Mono ) # Camera support status @@ -61,11 +63,13 @@ declare -A CAMERA_STATUS=( ["imx477"]="DEPRECATED" ["imx296"]="SUPPORTED" ["imx708"]="UNSUPPORTED" + ["arducam-pivariety"]="EXPERIMENTAL" ) # IMX296 specific types PITRAC_TYPE_PI_GS=4 # Raspberry Pi Global Shutter (color) PITRAC_TYPE_INNOMAKER=5 # InnoMaker IMX296 (mono) +PITRAC_TYPE_MIRA220=6 # Arducam Mira220 Mono # Device tree root paths DT_ROOT="/sys/firmware/devicetree/base" @@ -440,6 +444,12 @@ detect_cameras() { fi fi + # Special handling for Arducam Mira220 (arducam-pivariety driver) + if [[ "$sensor" == "arducam-pivariety" ]]; then + pitrac_type=$PITRAC_TYPE_MIRA220 + description="Arducam Mira220 (Mono)" + fi + CAMERA_PITRAC_TYPE["$idx"]="$pitrac_type" CAMERA_DESCRIPTION["$idx"]="$description" diff --git a/packaging/scripts/configure-cameras.sh b/packaging/scripts/configure-cameras.sh index 99c8155c..08bc3da6 100755 --- a/packaging/scripts/configure-cameras.sh +++ b/packaging/scripts/configure-cameras.sh @@ -153,10 +153,37 @@ configure_boot_config() { log_warn "No cameras detected. Camera-specific overlays will be skipped, but base system parameters will be configured." fi + # If Mira220 was not auto-detected (needs its overlay present at boot time), + # ask the user if one is connected so we can add the overlay now. + if [[ "$slot1_type" != "6" ]] && [[ "$slot2_type" != "6" ]]; then + if [[ "$num_cameras" -le 1 ]]; then + echo "" + log_info "PiTrac supports the Arducam Mira220 (type 6) global-shutter camera." + log_info "This camera requires a dtoverlay to be set before libcamera can detect it." + log_info "If you have a Mira220 connected, answer 'y' to add the overlay now." + read -r -p "Do you have an Arducam Mira220 camera connected? [y/N]: " has_mira220 + if [[ "$has_mira220" =~ ^[Yy]$ ]]; then + preserved_mira220_overlay="dtoverlay=arducam-pivariety" + log_info "Will add arducam-pivariety overlay. Reboot required for detection." + fi + fi + fi + backup_config_txt "$config_path" log_info "Configuring ${config_path}..." + # Before wiping, check if the old PiTrac block contains a Mira220 overlay. + # The Mira220 requires its overlay to be loaded before libcamera can detect + # it. If we blindly delete the block, we create a chicken-and-egg: without + # overlay the camera is invisible, so the detector reports 1 camera, so we + # write single-camera config, so the overlay stays missing on next boot. + local preserved_mira220_overlay="" + if sed -n '/# PiTrac Camera Configuration/,/# End PiTrac Camera Configuration/p' "$config_path" 2>/dev/null | grep -q 'dtoverlay=arducam-pivariety'; then + preserved_mira220_overlay="dtoverlay=arducam-pivariety" + log_info "Preserving existing arducam-pivariety overlay from old config" + fi + log_info "Removing existing PiTrac configuration (if present)..." sed -i '/# PiTrac Camera Configuration/,/# End PiTrac Camera Configuration/d' "$config_path" 2>/dev/null || true @@ -210,22 +237,47 @@ arm_boost=1" fi if [[ "$num_cameras" -eq 2 ]]; then + local cam1_overlay="dtoverlay=imx296,sync-sink" + if [[ "$slot2_type" == "6" ]]; then + cam1_overlay="dtoverlay=arducam-pivariety" + elif [[ "$slot1_type" == "6" ]]; then + cam1_overlay="dtoverlay=arducam-pivariety" + fi config_block="$config_block # Dual camera configuration (single-pi system) # Camera 0: internal trigger, Camera 1: external trigger [all] dtoverlay=imx296,cam0 -dtoverlay=imx296,sync-sink" +${cam1_overlay}" elif [[ "$num_cameras" -eq 1 ]]; then - config_block="$config_block + # If only one camera is detected but the old config had a Mira220 + # overlay, the Mira220 is probably still connected — its overlay just + # wasn't loaded this boot, making it invisible to libcamera. Write a + # dual-camera config so the overlay is present on next boot. + if [[ -n "$preserved_mira220_overlay" ]]; then + config_block="$config_block + +# Dual camera configuration (single-pi system) +# Camera 0: internal trigger, Camera 1: Mira220 external trigger (preserved) +[all] +dtoverlay=imx296,cam0 +${preserved_mira220_overlay}" + log_info "Single camera detected, but preserving arducam-pivariety overlay for second camera" + else + local single_overlay="dtoverlay=imx296,cam0" + if [[ "$slot1_type" == "6" ]]; then + single_overlay="dtoverlay=arducam-pivariety,cam0" + fi + config_block="$config_block # Single camera configuration [all] -dtoverlay=imx296,cam0" +${single_overlay}" + fi fi - if [[ "$has_innomaker" == "true" ]]; then + if [[ "$has_innomaker" == "true" ]] && [[ "$slot1_type" != "6" ]] && [[ "$slot2_type" != "6" ]]; then config_block="$config_block # InnoMaker IMX296 camera support diff --git a/packaging/src/lib/pitrac-common-functions.sh b/packaging/src/lib/pitrac-common-functions.sh index 751dd09f..38ab11bc 100755 --- a/packaging/src/lib/pitrac-common-functions.sh +++ b/packaging/src/lib/pitrac-common-functions.sh @@ -320,7 +320,6 @@ install_camera_tools() { local camera_tools_dir="$repo_root/Software/LMSourceCode/ImageProcessing/CameraTools" local imaging_dir="$repo_root/Software/LMSourceCode/ImageProcessing" - if [[ ! -d "$camera_tools_dir" ]]; then log_warn "Camera tools not found: $camera_tools_dir" return @@ -344,6 +343,9 @@ install_camera_tools() { if [[ -f "$tools_dest/imx296_trigger" ]]; then chmod 755 "$tools_dest/imx296_trigger" fi + if [[ -f "$tools_dest/v4l2_trigger" ]]; then + chmod 755 "$tools_dest/v4l2_trigger" + fi log_success "Camera tools installed" } diff --git a/packaging/templates/postinst.sh b/packaging/templates/postinst.sh index 7f89836f..b3d11f7e 100644 --- a/packaging/templates/postinst.sh +++ b/packaging/templates/postinst.sh @@ -140,27 +140,21 @@ case "$1" in # Install sensor file for detected Pi model install_imx296_sensor_file "$PI_MODEL" - # Use existing example.yaml files as base for configuration - # Both Pi 4 (vc4) and Pi 5 (pisp) ship with example.yaml + # Use existing example.yaml files as base for configuration. + # Both Pi 4 (vc4) and Pi 5 (pisp) ship with example.yaml. + # Copy it as rpi_apps.yaml WITHOUT uncommenting camera_timeout_value_ms. + # The default (computed by libcamera from the sensor frame rate) is fine; + # the application already sets a per-frame timeout of 100000s via + # options->Set().timeout.set("100000s") to handle external-trigger waits. for pipeline in pisp vc4; do CAMERA_DIR="/usr/share/libcamera/pipeline/rpi/${pipeline}" EXAMPLE_FILE="${CAMERA_DIR}/example.yaml" CAMERA_CONFIG="${CAMERA_DIR}/rpi_apps.yaml" - # Only proceed if the pipeline directory exists (Pi 4 has vc4, Pi 5 has pisp) if [ -d "$CAMERA_DIR" ]; then - # If example exists but rpi_apps doesn't, copy and configure if [ -f "$EXAMPLE_FILE" ] && [ ! -f "$CAMERA_CONFIG" ]; then echo "Creating ${pipeline} config from example..." cp "$EXAMPLE_FILE" "$CAMERA_CONFIG" - # Uncomment and set the camera timeout to 24 hours (86400000 ms) - sed -i 's/# *"camera_timeout_value_ms": *[0-9][0-9]*/"camera_timeout_value_ms": 86400000/' "$CAMERA_CONFIG" - elif [ -f "$CAMERA_CONFIG" ]; then - # Config exists, check if timeout needs updating - if grep -q '# *"camera_timeout_value_ms"' "$CAMERA_CONFIG"; then - echo "Updating ${pipeline} camera timeout..." - sed -i 's/# *"camera_timeout_value_ms": *[0-9][0-9]*/"camera_timeout_value_ms": 86400000/' "$CAMERA_CONFIG" - fi fi fi done