     {:noreply, state}
   end
   
+  def handle_cast({type, _}, state={_, :not_ready}) when 
+    is_atom(type) and type in [:send, :send_read] do
+    
+    msg = "serial port has not been setup, call open_port"
+    
+    Logger.error msg
+    {:noreply, state}
+  end
+  
+  def handle_call({:open, _, _}, _from, state={_, :ready}) do
+    msg = "port is already open" 
+    
+    Logger.error msg
+    {:reply, {:error, msg}, state}
+  end
+  
+  def handle_call({:open, port_name, baud_rate}, _from, {port, :not_ready}) do
+    Logger.debug "opening serial port"
+    
+    Port.command(port, :erlang.term_to_binary({:open, port_name, baud_rate}))
+    
+    {:reply, :ok, {port, :ready}}
+  end
+  
   def handle_info({port, {:data, data}} , state={port, _}) do
     msg = :erlang.binary_to_term(data)
     Logger.debug "received - #{msg}"


  # private 
  #----------------------------------------------------------------------------

  defp create_command(:start) do
    Logger.debug "start"

    {:ok, {port, :not_ready}}
  end 

  @doc """
    do_commands takes a list of commands like
    [{:drive, 100, :straight}, {:sleep, 1000}, {:drive, 0, :straight}]

    walks the list trasnforming taking the head of the list, transforming it
    into the form needed to send to the roomba then call do_commands until the
    list is empty. 

    sleep is a special case where we don't have to call out to the roomba
    instead we use apply_after below to continue with the next item in the list
    after timout value. So the in example above you drive straight for 1 second
    and then stop. 
  """
  def handle_cast({:do_commands, commands}, state) do
    case get_command commands do
      :ok -> 
        {:noreply, state}
      {:ok, {:sleep, value}, rest} ->
        Logger.debug "sleeping for #{value}"
        :timer.apply_after(value, GenServer, :cast, [:roomba_server,
                                                    {:do_commands, rest}])
        {:noreply, state}
      {:ok, cmd, rest} ->
        GenServer.cast(:roomba_server, cmd)
        GenServer.cast(:roomba_server, {:do_commands, rest})

        {:noreply, state}
    end
  end

  @doc """
    Both send and send_read send message to the c port that talks
    to the serial port. only distinguished by the atom :send/:send_read
    which tells the port that it should either just send the data down the wire
    and return or, send and read back the bytes sent from the roomba. 

    Roomba only sends back information when a sensor command was sent to it.
  """
  def handle_cast({:send, command}, state={port, :ready}) do
    hexified = Hexate.encode command
    Logger.debug "sending command - #{hexified}"
