1
0
tuanchris 3 сар өмнө
parent
commit
03e7dd7781

+ 13 - 1
main.py

@@ -385,23 +385,35 @@ async def get_angular_homing():
     """Get current angular homing settings."""
     return {
         "angular_homing_enabled": state.angular_homing_enabled,
+        "angular_homing_gpio_pin": state.angular_homing_gpio_pin,
+        "angular_homing_invert_state": state.angular_homing_invert_state,
         "angular_homing_offset_degrees": state.angular_homing_offset_degrees
     }
 
 class AngularHomingRequest(BaseModel):
     angular_homing_enabled: bool
+    angular_homing_gpio_pin: int = 18
+    angular_homing_invert_state: bool = False
     angular_homing_offset_degrees: float = 0.0
 
 @app.post("/api/angular-homing")
 async def set_angular_homing(request: AngularHomingRequest):
     """Update angular homing settings."""
     try:
+        # Validate GPIO pin
+        if request.angular_homing_gpio_pin < 2 or request.angular_homing_gpio_pin > 27:
+            raise HTTPException(status_code=400, detail="GPIO pin must be between 2 and 27")
+
         state.angular_homing_enabled = request.angular_homing_enabled
+        state.angular_homing_gpio_pin = request.angular_homing_gpio_pin
+        state.angular_homing_invert_state = request.angular_homing_invert_state
         state.angular_homing_offset_degrees = request.angular_homing_offset_degrees
         state.save()
 
-        logger.info(f"Angular homing {'enabled' if request.angular_homing_enabled else 'disabled'}, offset: {request.angular_homing_offset_degrees}°")
+        logger.info(f"Angular homing {'enabled' if request.angular_homing_enabled else 'disabled'}, GPIO pin: {request.angular_homing_gpio_pin}, invert: {request.angular_homing_invert_state}, offset: {request.angular_homing_offset_degrees}°")
         return {"success": True, "message": "Angular homing settings updated"}
+    except HTTPException:
+        raise
     except Exception as e:
         logger.error(f"Error updating angular homing settings: {str(e)}")
         raise HTTPException(status_code=500, detail=f"Failed to update angular homing settings: {str(e)}")

+ 5 - 2
modules/connection/connection_manager.py

@@ -463,8 +463,11 @@ def home(timeout=90):
             if state.angular_homing_enabled:
                 logger.info("Starting angular homing sequence")
                 try:
-                    # Initialize reed switch monitor
-                    reed_switch = ReedSwitchMonitor(gpio_pin=18)
+                    # Initialize reed switch monitor with configured GPIO pin and invert state
+                    gpio_pin = state.angular_homing_gpio_pin
+                    invert_state = state.angular_homing_invert_state
+                    logger.info(f"Using GPIO pin {gpio_pin} for reed switch (invert_state={invert_state})")
+                    reed_switch = ReedSwitchMonitor(gpio_pin=gpio_pin, invert_state=invert_state)
 
                     try:
                         # Reset theta first

+ 19 - 6
modules/connection/reed_switch.py

@@ -10,14 +10,16 @@ logger = logging.getLogger(__name__)
 class ReedSwitchMonitor:
     """Monitor a reed switch connected to a Raspberry Pi GPIO pin."""
 
-    def __init__(self, gpio_pin=18):
+    def __init__(self, gpio_pin=18, invert_state=False):
         """
         Initialize the reed switch monitor.
 
         Args:
             gpio_pin: GPIO pin number (BCM numbering) for the reed switch
+            invert_state: If True, invert the logic (triggered = LOW instead of HIGH)
         """
         self.gpio_pin = gpio_pin
+        self.invert_state = invert_state
         self.gpio = None
         self.is_raspberry_pi = False
 
@@ -34,7 +36,7 @@ class ReedSwitchMonitor:
             self.gpio.setup(self.gpio_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
             self.is_raspberry_pi = True
-            logger.info(f"Reed switch initialized on GPIO pin {self.gpio_pin}")
+            logger.info(f"Reed switch initialized on GPIO pin {self.gpio_pin} (invert_state={self.invert_state})")
         except ImportError:
             logger.warning("RPi.GPIO not available. Reed switch monitoring disabled.")
         except Exception as e:
@@ -46,15 +48,26 @@ class ReedSwitchMonitor:
         Check if the reed switch is currently triggered.
 
         Returns:
-            bool: True if reed switch is triggered (pin is HIGH), False otherwise
+            bool: True if reed switch is triggered, False otherwise
+
+        Notes:
+            - If invert_state=False: triggered when pin is HIGH (1)
+            - If invert_state=True: triggered when pin is LOW (0)
         """
         if not self.is_raspberry_pi or not self.gpio:
             return False
 
         try:
-            # Pin is HIGH (1) when reed switch is closed (triggered)
-            # This assumes the switch connects the pin to 3.3V when closed
-            return self.gpio.input(self.gpio_pin) == 1
+            # Read the GPIO pin state
+            pin_state = self.gpio.input(self.gpio_pin)
+
+            # Apply inversion if configured
+            if self.invert_state:
+                # Inverted: triggered when LOW (0)
+                return pin_state == 0
+            else:
+                # Normal: triggered when HIGH (1)
+                return pin_state == 1
         except Exception as e:
             logger.error(f"Error reading reed switch: {e}")
             return False

+ 8 - 0
modules/core/state.py

@@ -36,6 +36,10 @@ class AppState:
         self.homing = 0
         # Angular homing with reed switch (Raspberry Pi only)
         self.angular_homing_enabled = False
+        # GPIO pin number (BCM numbering) for reed switch
+        self.angular_homing_gpio_pin = 18
+        # Invert the reed switch state (False = triggered on HIGH, True = triggered on LOW)
+        self.angular_homing_invert_state = False
         # Angular offset in degrees for reed switch sensor placement
         # This allows correcting for the physical position of the reed switch
         self.angular_homing_offset_degrees = 0.0
@@ -185,6 +189,8 @@ class AppState:
             "gear_ratio": self.gear_ratio,
             "homing": self.homing,
             "angular_homing_enabled": self.angular_homing_enabled,
+            "angular_homing_gpio_pin": self.angular_homing_gpio_pin,
+            "angular_homing_invert_state": self.angular_homing_invert_state,
             "angular_homing_offset_degrees": self.angular_homing_offset_degrees,
             "current_playlist": self._current_playlist,
             "current_playlist_name": self._current_playlist_name,
@@ -226,6 +232,8 @@ class AppState:
         self.gear_ratio = data.get('gear_ratio', 10)
         self.homing = data.get('homing', 0)
         self.angular_homing_enabled = data.get('angular_homing_enabled', False)
+        self.angular_homing_gpio_pin = data.get('angular_homing_gpio_pin', 18)
+        self.angular_homing_invert_state = data.get('angular_homing_invert_state', False)
         self.angular_homing_offset_degrees = data.get('angular_homing_offset_degrees', 0.0)
         self._current_playlist = data.get("current_playlist", None)
         self._current_playlist_name = data.get("current_playlist_name", None)

+ 26 - 1
static/js/settings.js

@@ -1373,12 +1373,18 @@ async function initializeAngularHomingConfig() {
 
     const angularHomingToggle = document.getElementById('angularHomingToggle');
     const angularHomingInfo = document.getElementById('angularHomingInfo');
+    const gpioSelectionContainer = document.getElementById('gpioSelectionContainer');
+    const gpioInput = document.getElementById('gpioInput');
+    const invertStateContainer = document.getElementById('invertStateContainer');
+    const invertStateToggle = document.getElementById('invertStateToggle');
     const angularOffsetContainer = document.getElementById('angularOffsetContainer');
     const angularOffsetInput = document.getElementById('angularOffsetInput');
     const saveHomingConfigButton = document.getElementById('saveHomingConfig');
 
     // Check if elements exist
-    if (!angularHomingToggle || !angularHomingInfo || !saveHomingConfigButton || !angularOffsetContainer || !angularOffsetInput) {
+    if (!angularHomingToggle || !angularHomingInfo || !saveHomingConfigButton ||
+        !gpioSelectionContainer || !gpioInput || !invertStateContainer ||
+        !invertStateToggle || !angularOffsetContainer || !angularOffsetInput) {
         logMessage('Angular Homing elements not found, skipping initialization', LOG_TYPE.WARNING);
         return;
     }
@@ -1391,23 +1397,38 @@ async function initializeAngularHomingConfig() {
         const data = await response.json();
 
         angularHomingToggle.checked = data.angular_homing_enabled || false;
+        gpioInput.value = data.angular_homing_gpio_pin || 18;
+        invertStateToggle.checked = data.angular_homing_invert_state || false;
         angularOffsetInput.value = data.angular_homing_offset_degrees || 0;
 
         if (data.angular_homing_enabled) {
             angularHomingInfo.style.display = 'block';
+            gpioSelectionContainer.style.display = 'block';
+            invertStateContainer.style.display = 'block';
             angularOffsetContainer.style.display = 'block';
         }
     } catch (error) {
         logMessage(`Error loading angular homing settings: ${error.message}`, LOG_TYPE.ERROR);
         // Initialize with defaults if load fails
         angularHomingToggle.checked = false;
+        gpioInput.value = 18;
+        invertStateToggle.checked = false;
         angularOffsetInput.value = 0;
         angularHomingInfo.style.display = 'none';
+        gpioSelectionContainer.style.display = 'none';
+        invertStateContainer.style.display = 'none';
         angularOffsetContainer.style.display = 'none';
     }
 
     // Function to save settings
     async function saveAngularHomingSettings() {
+        // Validate GPIO pin
+        const gpioPin = parseInt(gpioInput.value);
+        if (isNaN(gpioPin) || gpioPin < 2 || gpioPin > 27) {
+            showStatusMessage('GPIO pin must be between 2 and 27', 'error');
+            return;
+        }
+
         // Update button UI to show loading state
         const originalButtonHTML = saveHomingConfigButton.innerHTML;
         saveHomingConfigButton.disabled = true;
@@ -1419,6 +1440,8 @@ async function initializeAngularHomingConfig() {
                 headers: { 'Content-Type': 'application/json' },
                 body: JSON.stringify({
                     angular_homing_enabled: angularHomingToggle.checked,
+                    angular_homing_gpio_pin: gpioPin,
+                    angular_homing_invert_state: invertStateToggle.checked,
                     angular_homing_offset_degrees: parseFloat(angularOffsetInput.value) || 0
                 })
             });
@@ -1452,6 +1475,8 @@ async function initializeAngularHomingConfig() {
         logMessage(`Angular homing toggle changed: ${angularHomingToggle.checked}`, LOG_TYPE.INFO);
         const isEnabled = angularHomingToggle.checked;
         angularHomingInfo.style.display = isEnabled ? 'block' : 'none';
+        gpioSelectionContainer.style.display = isEnabled ? 'block' : 'none';
+        invertStateContainer.style.display = isEnabled ? 'block' : 'none';
         angularOffsetContainer.style.display = isEnabled ? 'block' : 'none';
         logMessage(`Info display set to: ${angularHomingInfo.style.display}`, LOG_TYPE.INFO);
     });

+ 46 - 4
templates/settings.html

@@ -322,7 +322,7 @@ input:checked + .slider:before {
             Angular Homing (Raspberry Pi Only)
           </h3>
           <p class="text-xs text-slate-500 mt-1">
-            Enable angular homing using a reed switch on GPIO 18 to establish a home position for rotation.
+            Enable angular homing using a reed switch connected to a GPIO pin to establish a home position for rotation.
           </p>
         </div>
         <label class="switch">
@@ -331,6 +331,46 @@ input:checked + .slider:before {
         </label>
       </div>
 
+      <!-- GPIO Pin Selection (shown when angular homing is enabled) -->
+      <div id="gpioSelectionContainer" class="space-y-2" style="display: none;">
+        <label for="gpioInput" class="text-sm font-medium text-slate-700 flex items-center gap-2">
+          <span class="material-icons text-slate-600 text-base">settings_input_component</span>
+          GPIO Pin Number
+        </label>
+        <input
+          type="number"
+          id="gpioInput"
+          min="2"
+          max="27"
+          step="1"
+          value="18"
+          class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
+          placeholder="18"
+        />
+        <p class="text-xs text-slate-500">
+          GPIO pin number (BCM numbering) where the reed switch is connected. Common pins: 17, 18, 22, 23, 24, 25, 27.
+        </p>
+      </div>
+
+      <!-- Invert State Toggle (shown when angular homing is enabled) -->
+      <div id="invertStateContainer" class="space-y-2" style="display: none;">
+        <div class="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
+          <div class="flex-1">
+            <label for="invertStateToggle" class="text-sm font-medium text-slate-700 flex items-center gap-2 cursor-pointer">
+              <span class="material-icons text-slate-600 text-base">swap_vert</span>
+              Invert Reed Switch State
+            </label>
+            <p class="text-xs text-slate-500 mt-1">
+              Enable if your reed switch is triggered when LOW instead of HIGH (normally closed configuration).
+            </p>
+          </div>
+          <label class="switch">
+            <input type="checkbox" id="invertStateToggle">
+            <span class="slider round"></span>
+          </label>
+        </div>
+      </div>
+
       <!-- Angular Offset Input (shown when angular homing is enabled) -->
       <div id="angularOffsetContainer" class="space-y-2" style="display: none;">
         <label for="angularOffsetInput" class="text-sm font-medium text-slate-700 flex items-center gap-2">
@@ -359,10 +399,12 @@ input:checked + .slider:before {
             <p class="font-medium text-blue-800">Angular Homing Details:</p>
             <ul class="mt-1 space-y-1 text-blue-700">
               <li>• After radial homing, the arm moves to the perimeter (y20)</li>
-              <li>• The table rotates around the perimeter until the reed switch on GPIO 18 is triggered</li>
+              <li>• The table rotates around the perimeter until the reed switch is triggered</li>
               <li>• This position is set as the angular home based on your sensor offset</li>
-              <li>• Only works on Raspberry Pi with a reed switch connected to GPIO 18</li>
-              <li>• Reed switch should connect GPIO 18 to ground when triggered</li>
+              <li>• Only works on Raspberry Pi with a reed switch connected to your selected GPIO pin</li>
+              <li>• Standard wiring: Reed switch connects GPIO pin to 3.3V (triggered = HIGH)</li>
+              <li>• Inverted wiring: Reed switch connects GPIO pin to ground (triggered = LOW)</li>
+              <li>• Use BCM GPIO numbering (not physical pin numbers)</li>
             </ul>
           </div>
         </div>

+ 76 - 9
test_reed_switch.py

@@ -1,14 +1,24 @@
 #!/usr/bin/env python3
 """
-Simple test script to verify reed switch functionality on GPIO 18.
+Simple test script to verify reed switch functionality.
 Run this script on your Raspberry Pi to test the reed switch.
 
 Usage:
-    python test_reed_switch.py
+    python test_reed_switch.py [--gpio PIN] [--invert]
+
+Arguments:
+    --gpio PIN    GPIO pin number (BCM numbering) to test (default: 18)
+    --invert      Invert the switch logic (triggered = LOW instead of HIGH)
+
+Examples:
+    python test_reed_switch.py                    # Test GPIO 18 (default, normal logic)
+    python test_reed_switch.py --gpio 17          # Test GPIO 17 (normal logic)
+    python test_reed_switch.py --gpio 22 --invert # Test GPIO 22 (inverted logic)
 """
 
 import time
 import sys
+import argparse
 
 try:
     from modules.connection.reed_switch import ReedSwitchMonitor
@@ -17,15 +27,30 @@ except ImportError:
     print("Make sure you're running this from the dune-weaver directory")
     sys.exit(1)
 
-def main():
+def main(gpio_pin=18, invert_state=False):
+    """
+    Test the reed switch on the specified GPIO pin.
+
+    Args:
+        gpio_pin: GPIO pin number (BCM numbering) to test
+        invert_state: If True, invert the switch logic (triggered = LOW)
+    """
     print("=" * 60)
-    print("Reed Switch Test - GPIO 18")
+    print(f"Reed Switch Test - GPIO {gpio_pin}")
+    if invert_state:
+        print("(Inverted Logic: Triggered = LOW)")
+    else:
+        print("(Normal Logic: Triggered = HIGH)")
     print("=" * 60)
     print()
 
     # Initialize the reed switch monitor
-    print("Initializing reed switch monitor on GPIO 18...")
-    reed_switch = ReedSwitchMonitor(gpio_pin=18)
+    print(f"Initializing reed switch monitor on GPIO {gpio_pin}...")
+    if invert_state:
+        print("Using inverted logic (triggered when pin is LOW)")
+    else:
+        print("Using normal logic (triggered when pin is HIGH)")
+    reed_switch = ReedSwitchMonitor(gpio_pin=gpio_pin, invert_state=invert_state)
 
     # Check if we're on a Raspberry Pi
     if not reed_switch.is_raspberry_pi:
@@ -42,8 +67,13 @@ def main():
     print()
     print("Instructions:")
     print("  • The reed switch should be connected:")
-    print("    - One terminal → GPIO 18")
-    print("    - Other terminal → Ground (any GND pin)")
+    print(f"    - One terminal → GPIO {gpio_pin}")
+    if invert_state:
+        print("    - Other terminal → Ground (for inverted logic)")
+        print("    - Pull-up resistor enabled internally")
+    else:
+        print("    - Other terminal → 3.3V (for normal logic)")
+        print("    - Or use internal pull-up and connect to ground")
     print()
     print("  • Bring a magnet close to the reed switch to trigger it")
     print("  • You should see 'TRIGGERED!' when the switch closes")
@@ -85,4 +115,41 @@ def main():
         print()
 
 if __name__ == "__main__":
-    main()
+    # Parse command-line arguments
+    parser = argparse.ArgumentParser(
+        description="Test reed switch functionality on Raspberry Pi GPIO pins",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        epilog="""
+Examples:
+  python test_reed_switch.py                    # Test GPIO 18 (normal logic)
+  python test_reed_switch.py --gpio 17          # Test GPIO 17 (normal logic)
+  python test_reed_switch.py --gpio 22 --invert # Test GPIO 22 (inverted logic)
+
+Note: Uses BCM GPIO numbering (not physical pin numbers)
+      Normal logic: Triggered when HIGH (connected to 3.3V)
+      Inverted logic: Triggered when LOW (connected to ground)
+        """
+    )
+    parser.add_argument(
+        '--gpio',
+        type=int,
+        default=18,
+        metavar='PIN',
+        help='GPIO pin number to test (BCM numbering, default: 18)'
+    )
+    parser.add_argument(
+        '--invert',
+        action='store_true',
+        help='Invert the switch logic (triggered = LOW instead of HIGH)'
+    )
+
+    args = parser.parse_args()
+
+    # Validate GPIO pin range
+    if args.gpio < 2 or args.gpio > 27:
+        print(f"❌ ERROR: GPIO pin must be between 2 and 27 (got {args.gpio})")
+        print("Valid GPIO pins: 2-27 (BCM numbering)")
+        sys.exit(1)
+
+    # Run the test
+    main(gpio_pin=args.gpio, invert_state=args.invert)