# How to Configure Pan/Tilt Control for AtomSwing via WebUI

> Easily configure AtomSwing pan/tilt control using the WebUI. Adjust sliders in Setting Vue to send move commands to the firmware MotorMove handler for precise positioning.

- Repository: [Mitsuru Nakada/atomcam_tools](https://github.com/mnakada/atomcam_tools)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Configure AtomSwing pan/tilt positioning through the Vue.js-based WebUI by adjusting sliders in [`Setting.vue`](https://github.com/mnakada/atomcam_tools/blob/main/Setting.vue), which dispatch `move` commands to the firmware's `MotorMove` handler in [`libcallback/command.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/command.c).**

The `mnakada/atomcam_tools` firmware extends Atom cameras with comprehensive pan/tilt support for the AtomSwing model. This functionality is exposed through an intuitive web interface that translates user interactions into precise motor commands. Understanding the configuration flow—from Vue.js components to low-level motor drivers—enables you to effectively control camera positioning and set up automated cruise points.

## Understanding the AtomSwing Pan/Tilt Architecture

The pan/tilt system operates through three distinct layers: the web frontend, a command dispatcher, and the hardware abstraction layer.

### Web UI Layer (Setting.vue)

The primary interface resides in [`web/source/vue/Setting.vue`](https://github.com/mnakada/atomcam_tools/blob/main/web/source/vue/Setting.vue), which renders interactive sliders using Element UI components. The template defines two `ElSlider` controls:

- **Tilt Slider**: Vertical orientation, range 0–180 degrees
- **Pan Slider**: Horizontal orientation, range 0–355 degrees

These sliders bind to the component's reactive `pan` and `tilt` data fields. When values change, the `@change` and `@input` events trigger the `Move` method, which constructs and dispatches the motor command.

### Command Dispatch Layer (command.c)

The firmware's command table in [`libcallback/command.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/command.c) maps text commands to handler functions:

```c
static const struct {
  const char *name;
  int (*handler)(int argc, char *argv[]);
} command_table[] = {
  { "move", &MotorMove },
  // ... other commands
};

```

When the WebUI sends a `move` command via WebSocket or HTTP CGI, the dispatcher parses the arguments and invokes `MotorMove`.

### Motor Driver Layer (motor.c)

The `MotorMove` function in [`libcallback/motor.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/motor.c) validates parameters and calls the Ingenic SDK:

```c
int MotorMove(int argc, char *argv[]) {
  float pan  = atof(argv[1]);   // 0-355
  float tilt = atof(argv[2]);   // 0-180
  int speed  = argc > 3 ? atoi(argv[3]) : 9;
  int pri    = argc > 4 ? atoi(argv[4]) : 2;
  
  return local_sdk_motor_move_abs_angle(pan, tilt, speed, 
                                        &motor_move_done,
                                        &motor_move_canceled, pri);
}

```

The `local_sdk_motor_move_abs_angle` function executes the hardware movement with the specified speed and priority.

## Configuring Pan/Tilt Position via the WebUI

### Using the Slider Controls

Access the **Camera** tab in the AtomSwing WebUI to reveal the pan/tilt controls. The interface displays:

1. **Vertical Tilt Slider**: Drag to set elevation (0° = down, 180° = up)
2. **Horizontal Pan Slider**: Drag to set azimuth (0°–355°)

The Vue.js component automatically clamps values to valid ranges and triggers movement:

```javascript
async Move() {
  if (!this.posValid || !this.moveDone) return;
  await this.Exec(`move ${this.pan} ${this.tilt} 5 3`, 'socket');
  this.StillImageInterval();
  
  // Request position confirmation after 3 seconds
  if (this.moveTimeout) clearTimeout(this.moveTimeout);
  this.moveTimeout = setTimeout(() => {
    this.moveTimeout = null;
    this.Exec('posrec');
  }, 3000);
}

```

The `Exec` method transmits the command via WebSocket (`socket` transport) for low-latency control.

### Understanding the Move Command Syntax

The `move` command accepts four parameters:

```bash
move <pan> <tilt> [speed] [priority]

```

- **pan**: Absolute horizontal angle (0–355 degrees)
- **tilt**: Absolute vertical angle (0–180 degrees)  
- **speed**: Movement velocity (0–9, where 9 is fastest; default 9)
- **priority**: Command precedence (0–9, where 0 is highest; default 2)

Lower priority values override ongoing movements. The WebUI defaults to speed `5` and priority `3` for smooth, user-initiated adjustments.

### Resetting to Initial Position

Click the **Initial Position** button to home the motors. This triggers the `MoveInit` method:

```javascript
MoveInit() {
  this.Exec('moveinit');
}

```

The `moveinit` command instructs the firmware to reset the motor's absolute coordinate system, typically moving to a mechanical home position (0°, 90°) depending on hardware calibration.

## Advanced Configuration: Cruise Points

### Setting Up Cruise Points in SettingCruise.vue

For automated patrol patterns, use the cruise list functionality in [`web/source/vue/SettingCruise.vue`](https://github.com/mnakada/atomcam_tools/blob/main/web/source/vue/SettingCruise.vue). This component allows you to:

1. Define multiple waypoints with specific pan/tilt coordinates
2. Set dwell time at each position
3. Synchronize selected waypoints with the main control sliders

When you select a cruise point from the list, the component updates the parent [`Setting.vue`](https://github.com/mnakada/atomcam_tools/blob/main/Setting.vue) state:

```javascript
// Synchronization logic in SettingCruise.vue
selectCruisePoint(point) {
  this.$parent.pan = point.pan;
  this.$parent.tilt = point.tilt;
  this.$parent.Move();
}

```

This ensures the visual sliders reflect the saved position before execution.

## Summary

- **Web Interface**: Configure pan/tilt through [`Setting.vue`](https://github.com/mnakada/atomcam_tools/blob/main/Setting.vue) sliders (pan 0–355°, tilt 0–180°) which trigger the `Move` method
- **Command Flow**: UI sends `move <pan> <tilt> [speed] [priority]` via WebSocket to [`libcallback/command.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/command.c), dispatched to `MotorMove` in [`libcallback/motor.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/motor.c)
- **Hardware Control**: `local_sdk_motor_move_abs_angle` executes absolute positioning with configurable speed (0–9) and priority levels
- **Homing**: Use the **Initial Position** button to issue `moveinit` and reset motor coordinates
- **Automation**: Define patrol routes through [`SettingCruise.vue`](https://github.com/mnakada/atomcam_tools/blob/main/SettingCruise.vue) by saving multiple pan/tilt waypoints

## Frequently Asked Questions

### What is the valid range for pan and tilt values in the AtomSwing WebUI?

The AtomSwing supports **pan values from 0 to 355 degrees** and **tilt values from 0 to 180 degrees**. These limits are enforced by the slider components in [`web/source/vue/Setting.vue`](https://github.com/mnakada/atomcam_tools/blob/main/web/source/vue/Setting.vue) and validated in the `MotorMove` function within [`libcallback/motor.c`](https://github.com/mnakada/atomcam_tools/blob/main/libcallback/motor.c) before calling the SDK.

### How does the priority parameter affect motor movement?

The **priority parameter** (0–9, default 2) determines command precedence when multiple movement requests occur simultaneously. Lower numbers indicate higher priority—commands with priority 0 will interrupt ongoing movements, while priority 9 commands may queue or be ignored if the motor is busy. The WebUI uses priority 3 for user-initiated moves to balance responsiveness with system stability.

### Can I control pan/tilt programmatically outside the WebUI?

Yes, you can send **raw text commands** to the firmware's command socket or HTTP CGI endpoint. The syntax follows the same pattern used by the WebUI: `move <pan> <tilt> [speed] [priority]`. For example, sending `move 180 90 9 0` via TCP socket or CGI script will center the camera at maximum speed with highest priority, bypassing the Vue.js interface entirely.

### What happens if the motor is already moving when a new command is issued?

Behavior depends on the **priority** of the new command relative to the executing command. If the new command has equal or lower priority (higher numeric value), it typically queues or is rejected. If it has higher priority (lower numeric value), the `local_sdk_motor_move_abs_angle` function cancels the current movement via the `motor_move_canceled` callback and begins the new positioning. The WebUI waits for `moveDone` to become true before issuing subsequent commands to prevent command flooding.