blob: cc5daf3acae7e10de927bbc7f76a397a3cce32b0 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advertising &mdash; Apache Mynewt latest documentation</title>
<link rel="shortcut icon" href="../../../../_static/mynewt-logo-only-newt32x32.png"/>
<link rel="stylesheet" href="../../../../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/sphinx_theme.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/bootstrap-3.0.3.min.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/v2.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/custom.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/restructuredtext.css" type="text/css" />
<link rel="stylesheet" href="../../../../_static/css/overrides.css" type="text/css" />
<link rel="index" title="Index"
href="../../../../genindex.html"/>
<link rel="search" title="Search" href="../../../../search.html"/>
<link rel="top" title="Apache Mynewt latest documentation" href="../../../../index.html"/>
<link rel="up" title="BLE Peripheral Project" href="../bleprph.html"/>
<link rel="next" title="GAP Event callbacks" href="bleprph-gap-event.html"/>
<link rel="prev" title="Characteristic Access" href="bleprph-chr-access.html"/>
<script src="../../../../_static/js/modernizr.min.js"></script>
<script>
(function(i, s, o, g, r, a, m) {
i["GoogleAnalyticsObject"] = r;
(i[r] =
i[r] ||
function() {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, "script", "//www.google-analytics.com/analytics.js", "ga");
ga("create", "UA-72162311-1", "auto");
ga("send", "pageview");
</script>
</head>
<body class="not-front page-documentation" role="document" >
<div id="wrapper">
<div class="container">
<div id="banner" class="row v2-main-banner">
<a class="logo-cell" href="/">
<img class="logo" src="../../../../_static/img/logo.png">
</a>
<div class="tagline-cell">
<h4 class="tagline">An OS to build, deploy and securely manage billions of devices</h4>
</div>
<div class="news-cell">
<div class="well">
<h4>Latest News:</h4> <a href="/download">Apache Mynewt 1.12.0, Apache NimBLE 1.7.0 </a> released April 4, 2024)
</div>
</div>
</div>
</div>
<header>
<nav id="navbar" class="navbar navbar-inverse" role="navigation">
<div class="container">
<!-- Collapsed navigation -->
<div class="navbar-header">
<!-- Expander button -->
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<!-- Expanded navigation -->
<div class="navbar-collapse collapse">
<!-- Main navigation -->
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/"><i class="fa fa-home" style="font-size: larger;"></i></a>
</li>
<li class="important">
<a href="/quick-start/">Quick Start</a>
</li>
<li>
<a href="/about/">About</a>
</li>
<li>
<a href="/talks/">Talks</a>
</li>
<li class="active">
<a href="/documentation/">Documentation</a>
</li>
<li>
<a href="/download/">Download</a>
</li>
<li>
<a href="/community/">Community</a>
</li>
<li>
<a href="/events/">Events</a>
</li>
</ul>
<!-- Search, Navigation and Repo links -->
<ul class="nav navbar-nav navbar-right">
</ul>
</div>
</div>
</nav>
</header>
<!-- STARTS MAIN CONTENT -->
<div id="main-content">
<div id="breadcrumb">
<div class="container">
<a href="/documentation/">Docs</a> /
<a href="../../../tutorials.html">Tutorials</a> /
<a href="../../ble.html">Bluetooth Low Energy</a> /
<a href="../bleprph.html">BLE Peripheral Project</a> /
Advertising
<div class="sourcelink">
<a href="https://github.com/apache/mynewt-documentation/edit/master/docs/tutorials/ble/bleprph/bleprph-sections/bleprph-adv.rst" class="icon icon-github"
rel="nofollow"> Edit on GitHub</a>
</div>
</div>
</div>
<!-- STARTS CONTAINER -->
<div class="container">
<!-- STARTS .content -->
<div id="content" class="row">
<!-- STARTS .container-sidebar -->
<div class="container-sidebar col-xs-12 col-sm-3">
<div id="docSidebar" class="sticky-container">
<div role="search" class="sphinx-search">
<form id="rtd-search-form" class="wy-form" action="../../../../search.html" method="get">
<input type="text" name="q" placeholder="Search documentation" class="search-documentation" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<!-- Note: only works when deployed -->
<select class="form-control" onchange="if (this.value) window.location.href=this.value">
<option value="/latest" selected>
Version: latest
</option>
<option value="/v1_12_0" >
Version: 1.12.0
</option>
<option value="/v1_11_0" >
Version: 1.11.0
</option>
<option value="/v1_10_0" >
Version: 1.10.0
</option>
<option value="/v1_9_0" selected="selected" >
Version: 1.9.0
</option>
<option value="/v1_8_0" >
Version: 1.8.0
</option>
<option value="/v1_7_0" >
Version: 1.7.0
</option>
<option value="/v1_6_0" >
Version: 1.6.0
</option>
<option value="/v1_5_0" >
Version: 1.5.0
</option>
<option value="/v1_4_0" >
Version: 1.4.0
</option>
<option value="/v1_3_0/os/introduction" >
Version: 1.3.0
</option>
<option value="/v1_2_0/os/introduction" >
Version: 1.2.0
</option>
<option value="/v1_1_0/os/introduction" >
Version: 1.1.0
</option>
<option value="/v1_0_0/os/introduction" >
Version: 1.0.0
</option>
<option value="/v0_9_0/os/introduction" >
Version: 0.9.0
</option>
</select>
<div class="region region-sidebar">
<div class="docs-menu">
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../../../../index.html">Introduction</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../get_started/index.html">Setup &amp; Get Started</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../concepts.html">Concepts</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="../../../tutorials.html">Tutorials</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="../../../blinky/blinky.html">Project Blinky</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../repo/add_repos.html">Working with repositories</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../slinky/project-slinky.html">Project Slinky for Remote Comms</a></li>
<li class="toctree-l2 current"><a class="reference internal" href="../../ble.html">Bluetooth Low Energy</a><ul class="current">
<li class="toctree-l3"><a class="reference internal" href="../../ble_bare_bones.html">Set up a bare bones NimBLE application</a></li>
<li class="toctree-l3"><a class="reference internal" href="../../ibeacon.html">BLE iBeacon</a></li>
<li class="toctree-l3"><a class="reference internal" href="../../eddystone.html">BLE Eddystone</a></li>
<li class="toctree-l3 current"><a class="reference internal" href="../bleprph.html">BLE Peripheral Project</a><ul class="current">
<li class="toctree-l4"><a class="reference internal" href="bleprph-svc-reg.html">Service Registration</a></li>
<li class="toctree-l4"><a class="reference internal" href="bleprph-chr-access.html">Characteristic Access</a></li>
<li class="toctree-l4 current"><a class="current reference internal" href="#">Advertising</a></li>
<li class="toctree-l4"><a class="reference internal" href="bleprph-gap-event.html">GAP Event callbacks</a></li>
<li class="toctree-l4"><a class="reference internal" href="bleprph-app.html">BLE Peripheral App</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="../../blehci_project.html">Use HCI access to NimBLE controller</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../../../lora/lorawanapp.html">LoRa</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../os_fundamentals/os_fundamentals.html">OS Fundamentals</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../devmgmt/devmgmt.html">Remote Device Management</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../sensors/sensors.html">Sensors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../tooling/tooling.html">Tooling</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../other/other.html">Other</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../../../../external_links.html">Third-party Resources</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../os/os_user_guide.html">OS User Guide</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../network/index.html">BLE User Guide</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../newt/index.html">Newt Tool Guide</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../newtmgr/index.html">Newt Manager Guide</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../mynewt_faq/index.html">Mynewt FAQ</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../../misc/index.html">Appendix</a></li>
</ul>
</div>
</div>
</div>
<!-- ENDS STICKY CONTAINER -->
</div>
<!-- ENDS .container-sidebar -->
<div class="col-xs-12 col-sm-9">
<div class="alert alert-warning">
<p>
Version 1.9.0 is not the most recent version of the
Apache Mynewt documentation. Click <a href="/latest">here</a> to
read the latest version.
</p>
</div>
<div class="">
<div class="rst-content">
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="advertising">
<h1>Advertising<a class="headerlink" href="#advertising" title="Permalink to this headline"></a></h1>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><p><a class="reference internal" href="#overview" id="id1">Overview</a></p></li>
<li><p><a class="reference internal" href="#setting-advertisement-data" id="id2">Setting advertisement data</a></p></li>
<li><p><a class="reference internal" href="#begin-advertising" id="id3">Begin advertising</a></p></li>
</ul>
</div>
<div class="section" id="overview">
<h2><a class="toc-backref" href="#id1">Overview</a><a class="headerlink" href="#overview" title="Permalink to this headline"></a></h2>
<p>A peripheral announces its presence to the world by broadcasting
advertisements. An advertisement typically contains additional
information about the peripheral sending it, such as the device name and
an abbreviated list of supported services. The presence of this
information helps a listening central to determine whether it is
interested in connecting to the peripheral. Advertisements are quite
limited in the amount of information they can contain, so only the most
important information should be included.</p>
<p>When a listening device receives an advertisement, it can choose to
connect to the peripheral, or query the sender for more information.
This second action is known as an <em>active scan</em>. A peripheral responds
to an active scan with some extra information that it couldn’t fit in
its advertisement. This additional information is known as <em>scan
response data</em>. <em>bleprph</em> does not configure any scan response data, so
this feature is not discussed in the remainder of this tutorial.</p>
<p><em>bleprph</em> constantly broadcasts advertisements until a central connects
to it. When a connection is terminated, <em>bleprph</em> resumes advertising.</p>
<p>Let’s take a look at <em>bleprph</em>’s advertisement code (<em>main.c</em>):</p>
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cm">/**</span>
<span class="cm"> * Enables advertising with the following parameters:</span>
<span class="cm"> * o General discoverable mode.</span>
<span class="cm"> * o Undirected connectable mode.</span>
<span class="cm"> */</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">bleprph_advertise</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">ble_hs_adv_fields</span><span class="w"> </span><span class="n">fields</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">rc</span><span class="p">;</span>
<span class="w"> </span><span class="cm">/* Set the advertisement data included in our advertisements. */</span>
<span class="w"> </span><span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fields</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="n">fields</span><span class="p">);</span>
<span class="w"> </span><span class="n">fields</span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">uint8_t</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">bleprph_device_name</span><span class="p">;</span>
<span class="w"> </span><span class="n">fields</span><span class="p">.</span><span class="n">name_len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">strlen</span><span class="p">(</span><span class="n">bleprph_device_name</span><span class="p">);</span>
<span class="w"> </span><span class="n">fields</span><span class="p">.</span><span class="n">name_is_complete</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="n">rc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ble_gap_adv_set_fields</span><span class="p">(</span><span class="o">&amp;</span><span class="n">fields</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">rc</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">BLEPRPH_LOG</span><span class="p">(</span><span class="n">ERROR</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;error setting advertisement data; rc=%d</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">rc</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="cm">/* Begin advertising. */</span>
<span class="w"> </span><span class="n">rc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ble_gap_adv_start</span><span class="p">(</span><span class="n">BLE_GAP_DISC_MODE_GEN</span><span class="p">,</span><span class="w"> </span><span class="n">BLE_GAP_CONN_MODE_UND</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">bleprph_on_connect</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">rc</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">BLEPRPH_LOG</span><span class="p">(</span><span class="n">ERROR</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;error enabling advertisement; rc=%d</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">rc</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Now let’s examine this code in detail.</p>
</div>
<div class="section" id="setting-advertisement-data">
<h2><a class="toc-backref" href="#id2">Setting advertisement data</a><a class="headerlink" href="#setting-advertisement-data" title="Permalink to this headline"></a></h2>
<p>A NimBLE peripheral specifies what information to include in its
advertisements with the following function:</p>
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span>
<span class="n">ble_gap_adv_set_fields</span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="nc">ble_hs_adv_fields</span><span class="w"> </span><span class="o">*</span><span class="n">adv_fields</span><span class="p">)</span>
</pre></div>
</div>
<p>The <em>adv_fields</em> argument specifies the fields and their contents to
include in subsequent advertisements. The Bluetooth <a class="reference external" href="https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=302735">Core Specification
Supplement</a>
defines a set of standard fields that can be included in an
advertisement; the member variables of the <em>struct ble_hs_adv_fields</em>
type correspond to these standard fields. Information that doesn’t fit
neatly into a standard field should be put in the <em>manufacturing
specific data</em> field.</p>
<p>As you can see in the above code listing, the
<code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">ble_hs_adv_fields</span></code> instance is allocated on the stack. It is
OK to use the stack for this struct and the data it references, as the
<code class="docutils literal notranslate"><span class="pre">ble_gap_adv_set_fields()</span></code> function makes a copy of all the
advertisement data before it returns. <em>bleprph</em> doesn’t take full
advantange of this; it stores its device name in a static array.</p>
<p>The code sets three members of the <em>struct ble_hs_adv_fields</em>
instance:</p>
<ul class="simple">
<li><p>name</p></li>
<li><p>name_len</p></li>
<li><p>name_is_complete</p></li>
</ul>
<p>The first two fields are used to communicate the device’s name and are
quite straight-forward. The third field requires some explanation.
Bluetooth specifies two name-related advertisement fields: <em>Shortened
Local Name</em> and <em>Complete Local Name</em>. Setting the <code class="docutils literal notranslate"><span class="pre">name_is_complete</span></code>
variable to 1 or 0 tells NimBLE which of these two fields to include in
advertisements. Some other advertisement fields also correspond to
multiple variables in the field struct, so it is a good idea to review
the <em>ble_hs_adv_fields</em> reference to make sure you get the details
right in your app.</p>
</div>
<div class="section" id="begin-advertising">
<h2><a class="toc-backref" href="#id3">Begin advertising</a><a class="headerlink" href="#begin-advertising" title="Permalink to this headline"></a></h2>
<p>An app starts advertising with the following function:</p>
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span>
<span class="n">ble_gap_adv_start</span><span class="p">(</span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">discoverable_mode</span><span class="p">,</span><span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">connectable_mode</span><span class="p">,</span>
<span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="o">*</span><span class="n">peer_addr</span><span class="p">,</span><span class="w"> </span><span class="kt">uint8_t</span><span class="w"> </span><span class="n">peer_addr_type</span><span class="p">,</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">hci_adv_params</span><span class="w"> </span><span class="o">*</span><span class="n">adv_params</span><span class="p">,</span>
<span class="w"> </span><span class="n">ble_gap_conn_fn</span><span class="w"> </span><span class="o">*</span><span class="n">cb</span><span class="p">,</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="n">cb_arg</span><span class="p">)</span>
</pre></div>
</div>
<p>This function allows a lot of flexibility, and it might seem daunting at
first glance. <em>bleprph</em> specifies a simple set of arguments that is
appropriate for most peripherals. When getting started on a typical
peripheral, we recommend you use the same arguments as <em>bleprph</em>, with
the exception of the last two (<em>cb</em> and <em>cb_arg</em>). These last two
arguments will be specific to your app, so let’s talk about them.</p>
<p><em>cb</em> is a callback function. It gets executed when a central connects to
your peripheral after receiving an advertisement. The <em>cb_arg</em> argument
gets passed to the <em>cb</em> callback. If your callback doesn’t need the
<em>cb_arg</em> parameter, you can do what <em>bleprph</em> does and pass <em>NULL</em>.
Once a connection is established, the <em>cb</em> callback becomes permanently
associated with the connection. All subsequent events related to the
connection are communicated to your app via calls to this callback
function. Connection callbacks are an important part of building a BLE
app, and we examine <em>bleprph</em>’s connection callback in detail in the
next section of this tutorial.</p>
<p><strong>One final note:</strong> Your peripheral automatically stops advertising when
a central connects to it. You can immediately resume advertising if you
want to allow another central to connect, but you will need to do so
explicitly by calling <code class="docutils literal notranslate"><span class="pre">ble_gap_adv_start()</span></code> again. Also, be aware
NimBLE’s default configuration only allows a single connection at a
time. NimBLE supports multiple concurrent connections, but you must
configure it to do so first.</p>
</div>
</div>
</div>
</div>
<div class="rst-footer-buttons row" role="navigation" aria-label="footer navigation">
<a href="bleprph-gap-event.html" class="btn btn-neutral float-right" title="GAP Event callbacks" accesskey="n">Next: GAP Event callbacks <span class="fa fa-arrow-circle-right"></span></a>
<a href="bleprph-chr-access.html" class="btn btn-neutral" title="Characteristic Access" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous: Characteristic Access</a>
</div>
</div>
</div>
</div>
<!-- ENDS CONTENT SECTION -->
</div>
<!-- ENDS .content -->
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col-xs-12">
<p class="copyright">Apache Mynewt is available under Apache License, version 2.0.</p>
</div>
<div class="col-xs-12">
<div class="logos">
<img src="../../../../_static/img/asf_logo_wide_small.png" alt="Apache" title="Apache">
<small class="footnote">
Apache Mynewt, Mynewt, Apache, the Apache feather logo, and the Apache Mynewt project logo are either
registered trademarks or trademarks of the Apache Software Foundation in the United States and other countries.
</small>
<a href="">
<img src="../../../../_static/img/add_to_slack.png" alt="Slack Icon" title="Join our Slack Community" />
</a>
</div>
</div>
</div>
</div>
</footer>
</div>
<!-- ENDS #wrapper -->
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'../../../../',
VERSION:'latest',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
LINK_SUFFIX: '.html'
};
</script>
<script type="text/javascript" src="../../../../_static/jquery.js"></script>
<script type="text/javascript" src="../../../../_static/underscore.js"></script>
<script type="text/javascript" src="../../../../_static/doctools.js"></script>
<script type="text/javascript" src="../../../../_static/js/bootstrap-3.0.3.min.js"></script>
<script type="text/javascript" src="../../../../_static/js/affix.js"></script>
<script type="text/javascript" src="../../../../_static/js/main.js"></script>
</body>
</html>