Search documentation of bb

# How to Deploy to Nerves

Copy Markdown View Source

Deploy your Beam Bots robot to embedded hardware using Nerves.

## Prerequisites

- Nerves tooling installed (see Nerves Installation)
- Supported hardware (Raspberry Pi, BeagleBone, etc.)

## Step 1: Create a Nerves Project

Generate a new Nerves project:

    mix nerves.new my_robot_firmware
    cd my_robot_firmware

## Step 2: Install Beam Bots with Igniter

Use the Igniter installer to add Beam Bots:

    mix igniter.install bb

This will:

- Add `bb` to your dependencies
- Create a `MyRobotFirmware.Robot` module with arm/disarm commands and a base link
- Add the robot to your application supervision tree
- Configure the formatter for the BB DSL

## Step 3: Add Hardware Drivers

Add servo driver dependencies for your hardware. For example, with a PCA9685 PWM controller:

    mix igniter.install bb_servo_pca9685

Or manually add to `mix.exs`:

    defp deps do
      [
        # ... existing deps ...
        {:bb_servo_pca9685, "~> 0.1"}
      ]
    end

Then run `mix deps.get`.

## Step 4: Configure Your Robot

Edit the generated robot module to add your hardware configuration:

    # lib/my_robot_firmware/robot.ex
    defmodule MyRobotFirmware.Robot do
      use BB

      # Add controller for your servo driver
      controllers do
        controller :pca9685, {BB.Servo.PCA9685.Controller, bus: "i2c-1", address: 0x40}
      end

      commands do
        command :arm do
          handler BB.Command.Arm
          allowed_states [:disarmed]
        end

        command :disarm do
          handler BB.Command.Disarm
          allowed_states [:idle]
        end
      end

      topology do
        link :base do
          joint :pan, type: :revolute do
            limit lower: ~u(-90 degree), upper: ~u(90 degree), velocity: ~u(60 degree_per_second)

            # Add actuator and sensor for each joint
            actuator :servo, {BB.Servo.PCA9685.Actuator, channel: 0, controller: :pca9685}
            sensor :position, {BB.Sensor.OpenLoopPositionEstimator, actuator: :servo}
          end

          joint :tilt, type: :revolute do
            limit lower: ~u(-45 degree), upper: ~u(45 degree), velocity: ~u(60 degree_per_second)

            actuator :servo, {BB.Servo.PCA9685.Actuator, channel: 1, controller: :pca9685}
            sensor :position, {BB.Sensor.OpenLoopPositionEstimator, actuator: :servo}
          end
        end
      end
    end

## Step 5: Configure Hardware

Set up hardware-specific configuration in your Nerves config:

    # config/target.exs
    config :nerves, :firmware, rootfs_overlay: "rootfs_overlay"

If using GPIO (e.g., for `bb_servo_pigpio`):

    config :pigpiox,
      gpio_port: 8888

## Step 6: Build and Deploy

Build the firmware:

    export MIX_TARGET=rpi4  # or your target
    mix deps.get
    mix firmware

Deploy to device:

    # First time (burn to SD card)
    mix burn

    # Updates over network
    mix upload my_robot.local

## Step 7: Connect and Control

SSH into the device:

    ssh my_robot.local

In IEx:

    alias MyRobotFirmware.Robot

    # Arm the robot
    {:ok, cmd} = Robot.arm()
    {:ok, :armed, _} = BB.Command.await(cmd)

    # Move joints
    BB.Actuator.set_position!(Robot, :servo, 0.5)

## Network Control

### With bb_liveview

Add the LiveView dashboard for web-based control:

    mix igniter.install bb_liveview

Then configure your router:

    # In router
    import BB.LiveView.Router
    scope "/" do
      pipe_through :browser
      bb_dashboard "/", MyRobotFirmware.Robot
    end

Access at `http://my_robot.local/`.

### With Custom GenServer

Create a TCP/UDP server for remote control:

    defmodule MyRobotFirmware.RemoteControl do
      use GenServer

      def start_link(opts) do
        GenServer.start_link(__MODULE__, opts, name: __MODULE__)
      end

      def init(_opts) do
        {:ok, socket} = :gen_tcp.listen(4000, [:binary, active: true, reuseaddr: true])
        {:ok, %{socket: socket}}
      end

      def handle_info({:tcp, _socket, data}, state) do
        case Jason.decode(data) do
          {:ok, %{"command" => "move", "joint" => joint, "position" => pos}} ->
            BB.Actuator.set_position!(MyRobotFirmware.Robot, String.to_atom(joint), pos)

          _ ->
            :ok
        end

        {:noreply, state}
      end
    end

## Hardware Watchdog

For safety-critical applications, add a hardware watchdog:

    defmodule MyRobotFirmware.Watchdog do
      use GenServer

      @heartbeat_pin 18
      @interval_ms 100

      def start_link(opts) do
        GenServer.start_link(__MODULE__, opts, name: __MODULE__)
      end

      def init(_opts) do
        {:ok, gpio} = Circuits.GPIO.open(@heartbeat_pin, :output)
        schedule_heartbeat()
        {:ok, %{gpio: gpio}}
      end

      def handle_info(:heartbeat, state) do
        Circuits.GPIO.write(state.gpio, 1)
        Process.sleep(1)
        Circuits.GPIO.write(state.gpio, 0)
        schedule_heartbeat()
        {:noreply, state}
      end

      defp schedule_heartbeat do
        Process.send_after(self(), :heartbeat, @interval_ms)
      end
    end

## Common Issues

### I2C Device Not Found

Check the bus name matches your hardware:

- Raspberry Pi: Usually `"i2c-1"`
- BeagleBone: May be `"i2c-2"`

Verify with:

    Circuits.I2C.detect_devices("i2c-1")

### GPIO Permissions

Ensure GPIO is accessible. On some targets, add udev rules or use the `nerves_runtime` GPIO interface.

### Network Not Available on Boot

The robot may start before network is ready. If using network control, handle connection failures gracefully:

    def init(_opts) do
      # Wait for network
      VintageNet.subscribe(["interface", "eth0", "connection"])
      {:ok, :waiting_for_network}
    end

    def handle_info({VintageNet, ["interface", "eth0", "connection"], _, :internet, _}, state) do
      # Network ready, start accepting connections
      {:noreply, start_server(state)}
    end

## Testing Locally

Test your robot code on your development machine before deploying:

    # Start in simulation mode
    {:ok, _} = BB.Supervisor.start_link(MyRobotFirmware.Robot, simulation: :kinematic)

## Adding More Robots

To add additional robots to the same firmware:

    mix bb.add_robot --robot MyRobotFirmware.Robots.SecondRobot

## Next Steps

- Add bb_liveview for web-based control
- Implement hardware safety with watchdog
- Consider OTA updates with NervesHub

← Previous Page How to Add a Custom Command
Next Page → How to Implement Safety Callbacks