<repo_name>Shrinks99/tomesh2<file_sep>/build.md
---
layout: mkdownpage
title: Build A Node
parent: Get Involved
order: 1
---
The following instructions will help you set up an encrypted mesh network on Raspberry Pi's. It takes about 15 minutes to set up a node with the Pi 3. Obviously, to have a mesh you will need more than one node.

Many models of Orange Pi hardware running [Armbian](https://www.armbian.com/) are also supported. The same installation steps can be followed, except you would flash the SD card with Armbian instead of Raspbian. See [Hardware Table](#hardware-table) for the full list of supported hardware.

## Set Up

1. Make sure you have the following items:

    * Raspberry Pi Zero, 1, 2, 3 (Pi 3 recommended), or for advanced users other [compatible hardware](#hardware-table)
    * An SD card that works with the Pi
-    * **Optional:** A USB WiFi adapter with [802.11s Mesh Point](https://github.com/o11s/open80211s/wiki/HOWTO) support, such as the [TP-LINK TL-WN722N](http://www.tp-link.com/en/products/details/TL-WN722N.html) or [Toplinkst TOP-GS07](https://github.com/tomeshnet/documents/blob/master/technical/20170208_mesh-point-with-topgs07-rt5572.md)

1. Flash the SD card with [Raspbian Stretch Lite](https://www.raspberrypi.org/downloads/raspbian/).

1. Create an empty file named **ssh** to enable SSH when the Pi boots:

    ```
    $ touch /path/to/sd/boot/ssh
    ```

1. Plug the SD card and USB WiFi adapter into the Pi.

1. Plug the Pi into your router, so it has connectivity to the Internet. SSH into the Pi with `ssh <EMAIL>` and password **<PASSWORD>**.

    **Optional:** There are other ways to connect, such as connecting the Pi to your computer and sharing Internet to it. Or if you have multiple Pi's connected to your router, find its IP with `nmap -sn 192.168.X.0/24` (where 192.168.X is your subnet) and SSH to the local IP assigned to the Pi you want to address `ssh pi@192.168.X.Y`.

1. In your SSH session, run `passwd` and change your login password. It is very important to choose a strong password so others cannot remotely access your Pi.

1. Run the following, then let the installation complete. After about 5 minutes the Pi will reboot:

    ```
    $ wget https://raw.githubusercontent.com/tomeshnet/prototype-cjdns-pi/master/scripts/install && chmod +x install && ./install
    ```

    The installation script can also install many optional features such as distributed applications and network analysis tools that are useful but non-essential to run a node. You can use flags to selectively enable them, or use the following command to install all optional features:

    ```
    $ wget https://raw.githubusercontent.com/tomeshnet/prototype-cjdns-pi/master/scripts/install && chmod +x install && WITH_MESH_POINT=true WITH_WIFI_AP=true WITH_IPFS=true WITH_PROMETHEUS_NODE_EXPORTER=true WITH_PROMETHEUS_SERVER=true WITH_GRAFANA=true WITH_H_DNS=true WITH_H_NTP=true WITH_FAKE_HWCLOCK=true WITH_EXTRA_TOOLS=true ./install
    ```

## Optional Features

| Feature Flag                    | HTTP Service Port                              | Description |
| :------------------------------ | :--------------------------------------------- | :---------- |
| `WITH_MESH_POINT`               | None                                           | Set to `true` if you have a suitable USB WiFi adapter and want to configure it as a 802.11s Mesh Point interface. |
| `WITH_WIFI_AP`                  | None                                           | Set to `true` if you have a Raspberry Pi 3 and want to configure the on-board WiFi as an Access Point. The default configuration routes all traffic to the Ethernet port `eth0`. |
| `WITH_IPFS`                     | **80**: HTTP-to-IPFS gateway at `/ipfs/HASH` | Set to `true` if you want to install [IPFS](https://ipfs.io). |
| `WITH_PROMETHEUS_NODE_EXPORTER` | **9100**: Node Exporter UI                     | Set to `true` if you want to install [Prometheus Node Exporter](https://github.com/prometheus/node_exporter) to report network metrics. |
| `WITH_PROMETHEUS_SERVER`        | **9090**: Prometheus Server UI                 | Set to `true` if you want to install [Prometheus Server](https://github.com/prometheus/prometheus) to collect network metrics. *Requires Prometheus Node Exporter.* |
| `WITH_GRAFANA`                  | **3000**: Grafana UI (login: admin/admin)      | Set to `true` if you want to install [Grafana](https://grafana.com) to display network metrics. *Requires Prometheus Server.* |
| `WITH_H_DNS`                    | None                                           | Set to `true` if you want to use Hyperboria-compatible DNS servers: `fc4d:c8e5:9efe:9ac2:8e72:fcf7:6ce8:39dc` and `fc6e:691e:dfaa:b992:a10a:7b49:5a1a:5e09` |
| `WITH_H_NTP`                    | None                                           | Set to `true` if you want to use a Hyperboria-compatible NTP server: `fc4d:c8e5:9efe:9ac2:8e72:fcf7:6ce8:39dc` |
| `WITH_FAKE_HWCLOCK`             | None                                           | Set to `true` if you want to force hwclock to store its time every 5 minutes. |
| `WITH_EXTRA_TOOLS`              | None                                           | Set to `true` if you want to install non-essential tools useful for network analysis: vim socat oping bmon iperf3 |

If you are connected to the WiFi Access Point, all HTTP services are available via `http://10.0.0.1:PORT` as well as the cjdns IPv6. To connect with the cjdns address, first note your node's fc00::/8 address from `status`, then navigate to `http://[fcaa:bbbb:cccc:dddd:eeee:0000:1111:2222]:PORT` from your browser.

## Check Status

1. Give the Pi about 15 seconds to reboot and SSH back into it. You should find the status of your mesh node automatically printed. You can also print this anytime by running `status`.

1. Verify that **cjdns Service** is active, and **Mesh Interface** (if applicable). The **NODE** section should display a single IPv6 address, that's the identity of your Pi in the cjdns mesh. The **PEERS** section should indicate a list of IPv6 addresses that are active peers to your node. This list will be empty, until you have another nearby node with the same set up.

## Network Benchmark

You can benchmark the network throughput with more than one node. Let's name our two Pi's **Hillary** and **Friend**.

1. SSH to Friend and note its IPv6.

1. Run `iperf3 -s` to start listening. Do not end the SSH session.

1. In another Terminal session, SSH to Hillary and run `iperf3 -c FRIEND_IPV6`. You should start seeing Hillary sending encrypted packets to her Friend. See [phillymesh/cjdns-optimizations](https://github.com/phillymesh/cjdns-optimizations) for expected throughput.

## Update & Uninstall

To uninstall the services, run `./prototype-cjdns-pi/scripts/uninstall`.

If you are updating, run the same uninstall script, but keep all configuration files and data directories when prompted, remove the **prototype-cjdns-pi** directory along with the **install** script, then repeat the last installation step.

## Experimental Support for Orange Pi

We are adding support for [Orange Pi](http://www.orangepi.org/) boards and have tested with the [Orange Pi Zero (Armbian nightly)](https://dl.armbian.com/orangepizero/nightly/), [Orange Pi One (Armbian nightly)](https://dl.armbian.com/orangepione/nightly/), and [Orange Pi Lite  (Armbian nightly)](https://dl.armbian.com/orangepilite/nightly/). Instead of flashing Raspbian, start with the Armbian nightly images linked above, then follow the same installation steps as the Raspberry Pi.

## Hardware Table

List of tested hardware:

| Hardware                  | Base OS         | [CJDNS Benchmark](https://github.com/phillymesh/cjdns-optimizations) (salsa20/poly1305, switching) | USB | Ethernet | Notes    |
| :-------------------------|:----------------|:---------------------------------------------------------------------------------------------------|:----|:---------|:---------|
| Raspberry Pi 3            | [Raspbian Lite](https://www.raspberrypi.org/downloads/raspbian/)         | 350k, 100k | 2       | 10/100 |                                       |
| Raspberry Pi 2            | [Raspbian Lite](https://www.raspberrypi.org/downloads/raspbian/)         | 150k,  50k | 2       | 10/100 |                                       |
| Raspberry Pi 1 A+         | [Raspbian Lite](https://www.raspberrypi.org/downloads/raspbian/)         |  35k,   -  | 1       | None   |                                       |
| Raspberry Pi 1 B+         | [Raspbian Lite](https://www.raspberrypi.org/downloads/raspbian/)         |  35k,   -  | 2       | 10/100 |                                       |
| Raspberry Pi Zero         | [Raspbian Lite](https://www.raspberrypi.org/downloads/raspbian/)         |  68k,  30k | 1*      | None   | *Need OTG Cable                       |
| Orange Pi Lite            | [Armbian Nightly](https://dl.armbian.com/orangepilite/nightly/)          | 198k,  76k | 2       | None   |                                       |
| Orange Pi One             | [Armbian Nightly](https://dl.armbian.com/orangepione/nightly/)           | 198k,  76k | 1       | 10/100 |                                       |
| Orange Pi Zero            | [Armbian Nightly](https://dl.armbian.com/orangepizero/nightly/)          | 148k,  56k | 1 (+2*) | 10/100 | *Additional USB available via headers |
| Orange Pi Zero Plus 2 H5  | [Armbian Nightly](https://dl.armbian.com/orangepizeroplus2-h5/nightly/)  | 142k,  92K | 0 (+2*) | None   | *USB available via headers            |

## Development

You can install from a specific tag or branch, such as `develop`, with:

```
$ wget https://raw.githubusercontent.com/tomeshnet/prototype-cjdns-pi/develop/scripts/install && chmod +x install && TAG_PROTOTYPE_CJDNS_PI=develop ./install
```

If you are developing on a forked repository, such as `me/prototype-cjdns-pi`, then:

```
$ wget https://raw.githubusercontent.com/me/prototype-cjdns-pi/develop/scripts/install && chmod +x install && GIT_PROTOTYPE_CJDNS_PI="https://github.com/me/prototype-cjdns-pi.git" TAG_PROTOTYPE_CJDNS_PI=develop ./install
```

To add a new module, use **scripts/ipfs/** as an example to:

* Create a `WITH_NEW_MODULE` tag
* Create **scripts/new-module/install** and **scripts/new-module/uninstall**
* Make corresponding references in the main **install**, **install2**, **status**, **uninstall** files

## Notes

* Your computer can be a node too! It will mesh with the Pi's over your router. See the [cjdns repository](https://github.com/cjdelisle/cjdns) on how to set this up.

* Original plan for this repository and early benchmark results are available in [the doc folder](https://github.com/tomeshnet/prototype-cjdns-pi/blob/master/docs/).
<file_sep>/js/map.js
var map = null;
var infowindow = null;
var markers = [];
var currentNodeListURL;
var circle = null;

function initialize() {
  //Current Node URL with random bits to make sure it doesnt get cached
  currentNodeListURL = document.getElementById('nodeURL').value + '?ramd=' + new Date();

  //Set options based on check box positions
  var filterActive = document.getElementById('chkActive').checked;
  var filterProposed = document.getElementById('chkProposed').checked;
  var zoomGroup = document.getElementById('chkGroup').checked;

  //Prepare default view and create map
  var mapOptions = {
    zoom: 12,
    center: new google.maps.LatLng(43.698136, -79.397593),
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    fullscreenControl: false,
    mapTypeControl: true,
    mapTypeControlOptions: {
      position: google.maps.ControlPosition.RIGHT_BOTTOM
    }
  };

  infowindow = new google.maps.InfoWindow({
    content: 'holding...'
  });

  map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);

  //Reset markers array
  markers = undefined;
  markers = [];

  //Pull and process node url
  $.getJSON(currentNodeListURL, function (data) {

    var nodeVisible;

    //loop through each node
    for (var key in data) {
      var results = data[key];

      //console.log(results);
      nodeVisible = 1; //Default all nodes to visible

      //Adjust visibility based on value and option variable
      if (results['status'] == 'active' && !filterActive) nodeVisible = 0;
      if (results['status'] == 'proposed' && !filterProposed) nodeVisible = 0;

      if (nodeVisible) {
        //prepare location point
        var lat = results['latitude'];
        var lng = results['longitude'];
        var myNodeLatLng = new google.maps.LatLng(lat, lng);
        var myNodeName = results['name'];
        //Call function to create (or update) marker
        var newNode = addMarker(map, results, myNodeName, myNodeLatLng);

        //If new node was created (rather then updated) add it to the marker array
        if (newNode)
          markers.push(newNode);
      }
    }

    //Clustering code to group markers that are very close together untill you zoom in (if option enabled)
    if (zoomGroup) {
      var mcOptions = {
        gridSize: 20,
        maxZoom: 15,
        imagePath: '/images/map/m'
      };
      var mc = new MarkerClusterer(map, markers, mcOptions);
    }
  });
}

//Find a marker witth a specific lat lng and dir combo.  Used so that we dont create a new marker but rather add info to the existing one.
function findMarker(lat, lng, dir) {
  for (var i = 0; i < markers.length; i++) {
    if (markers[i].position.lat() == lat &&
      markers[i].position.lng() == lng &&
      markers[i].direction == dir) {
      return markers[i];
    }
  }
  return undefined;
}

//Tries to find marker that already exists and updates it otherwise creates a new one
function addMarker(map, nodeResult, name, location) {

  //Specify the colour of the marker based on the status
  var nodeColor;
  if (nodeResult['status'] == 'active') {
    nodeColor = 'green';
  }
  if (nodeResult['status'] == 'proposed') {
    nodeColor = 'grey';
  }

  //Default to OMNI icon if no direction is given
  var ArrowDirection = 'omni';

  //If direction is given set it to the correct direction
  if (nodeResult['cardinalDirection'] != null) ArrowDirection = nodeResult['cardinalDirection'];
  if (nodeResult['cardinalDirectionAntenna'] != null) ArrowDirection = nodeResult['cardinalDirectionAntenna'];

  //Return formatted date for display
  var formattedDate = function () {
    var date = new Date(nodeResult['dateAdded']);
    var options = { year: 'numeric', month: 'long', day: 'numeric' };
    return date.toLocaleDateString('en-US', options);
  };
  var nodeStatus = nodeResult['status'].charAt(0).toUpperCase() + nodeResult['status'].slice(1);

  //Prepare the detail information for the marker
  var Description = '';
  Description = '<div class="markerPop">';
  Description += '<h1>' + name + '</h1>';
  Description += '<p>Status: ' + nodeStatus + '</p>';
  if (nodeResult['cardinalDirection']) Description += '<p>Direction: ' + nodeResult['cardinalDirection'] + '</p>';
  if (nodeResult['cardinalDirectionAntenna']) Description += '<p>Antenna Direction: ' + nodeResult['cardinalDirectionAntenna'] + '</p>';	
  if (nodeResult['floor']) Description += '<p>Floor: ' + nodeResult['floor'] + '</p>';
  if (nodeResult['IPV6Address']) Description += '<p>IPV6: ' + nodeResult['IPV6Address'] + '</p>';
  Description += '<p>Added: ' + formattedDate() + '</p>';
  Description += '</div>';

  //Check to see if the currenty direction,lat,lng combo exists
  var marker = findMarker(location.lat(), location.lng(), ArrowDirection);

  //Prepare the image used to display the direction arrow and node color
  var IMG = '/images/map/arrow-' + ArrowDirection.toLowerCase().replace(' ', '') + '-' + nodeColor + '.png';

  //If marker does not exists in position and direction, create it
  if (marker == undefined) {

    //Establish anchor point based on direction of arrow so arrow images dont overlap each other so that they dont fully overlap
    var x = 16;
    var y = 16;
    switch (ArrowDirection) {
    case 'North':
    case 'North East':
    case 'North West':
      y = 32;
      break;
    case 'South':
    case 'South East':
    case 'South West':
      y = 0;
      break;
    }
    switch (ArrowDirection) {
    case 'East':
    case 'North East':
    case 'South East':
      x = 0;
      break;
    case 'West':
    case 'North West':
    case 'South West':
      x = 32;
      break;
    }

    var imageAnchor = new google.maps.Point(x, y);

    //Create a new marker
    marker = new google.maps.Marker({
      position: location,
      map: map,
      title: name,
      icon: {
        url: IMG,
        anchor: imageAnchor
      },
      direction: ArrowDirection,
      html: Description
    });

    //Add listener to the marker for click
    google.maps.event.addListener(marker, 'click', function () {

      //Code adds a circle to identiy selected marker and 
      //Maybe even present a possible range
      if (typeof infowindow != 'undefined') infowindow.close();
      infowindow.setContent(this.html);
      infowindow.open(map, this);

      if (circle) {
        circle.setMap(null);
      }
      // Add circle overlay and bind to marker
      circle = new google.maps.Circle({
        map: map,
        radius: 40, // 10 miles in metres
        fillColor: '#AA0000'
      });
      circle.bindTo('center', marker, 'position');


    });
    //listeer to close window
    google.maps.event.addListener(map, 'click', function () {
      infowindow.close();
    });
    //Returns marker to identify it was created not modified
    return marker;

    //If marker already exists in direction and position, just add more information to the existing one.
  } else {
    if (marker.icon.url != IMG) {

      //Promot Scacked Marker is new node is better then the previous
      //IE: if inactive and active in same macker make sure node color is green

      //Update marker color if an active node exists in the "stack"
      var markerLevel = 0;
      if (marker.icon.url.search('-red.png') > 0) markerLevel = 1;
      if (marker.icon.url.search('-green.png') > 0) markerLevel = 2;
      var requestLevel = 0;
      if (IMG.search('-red.png') > 0) requestLevel = 1;
      if (IMG.search('-green.png') > 0) requestLevel = 2;
      if (requestLevel > markerLevel) {
        marker.icon.url = IMG;
      }

    }
    //Update marker
    marker.html = marker.html + Description;
    return undefined;
  }
}


/*******************
 Custom Marker Code
********************
Functions that deal with dialog box interaction
including GeoCoding and JSON Generation
*/

var customMarker = undefined;

//Plot new marker from entered coordinates
function addCustomMarker() {
  var lng = document.getElementById('customMarkerLng').value;
  var lat = document.getElementById('customMarkerLat').value;

  if (customMarker) {
    customMarker.setPosition(new google.maps.LatLng(lat, lng));
  } else {
    var location = new google.maps.LatLng(lat, lng);
    customMarker = new google.maps.Marker({
      position: location,
      map: map,
      title: 'New Location',
      draggable: true
    });

    //Event for marker after it has been dropped (end of drag and drop)
    google.maps.event.addListener(customMarker, 'dragend', function () {
      document.getElementById('customMarkerLng').value = customMarker.getPosition().lng();
      document.getElementById('customMarkerLat').value = customMarker.getPosition().lat();
      customMarkerGenerateJSON(); //Regenerate json data in case your looking at the json screen
    });
  }
  map.setCenter(new google.maps.LatLng(lat, lng));
}

//Attempt to GeoCode the marker based on an address
function customMarkerGeoCode() {
  var address = document.getElementById('customMarkerAddress').value;
  var geocoder = new google.maps.Geocoder();
  geocoder.geocode({
    'address': address
  }, function (results, status) {
    if (status == 'OK') {

      document.getElementById('customMarkerLng').value = results[0].geometry.location.lng();
      document.getElementById('customMarkerLat').value = results[0].geometry.location.lat();
      map.setCenter(results[0].geometry.location);
      addCustomMarker(document.getElementById('customMarkerLat').value, document.getElementById('customMarkerLng').value);
      $('div#customMarkerGeoCodeDiv').hide();
      $('div#customMarkerLocationDiv').show();
    } else {
      alert('Geocode was not successful for the following reason: ' + status);
    }
  });
}

function customMarkerShowJsonDialog() {
  $('div#customMarkerJSONDiv').show();
  $('div#customMarkerLocationDiv').hide();
  customMarkerGenerateJSON();
}

//Updates the text for the JSON data on the JSON screen
function customMarkerGenerateJSON() {

  var lng = document.getElementById('customMarkerLng').value;
  var lat = document.getElementById('customMarkerLat').value;
  var floor = document.getElementById('customMarkerFloor').value;
  var dir = document.getElementById('customMarkerDirection').value;
  var name = document.getElementById('customMarkerName').value;


  var currentJSONDate = (new Date()).toJSON();

  var sJSON = '<div class="box-header"><h2>JSON for node</h2></div><pre style="white-space: pre;margin-bottom:10px;">   {\n' +
    '     "name": "' + name + '",\n' +
    '     "latitude": ' + lat + ',\n' +
    '     "longitude":' + lng + ',\n' +
    '     "cardinalDirection": "' + dir + '",\n' +
    '     "floor": ' + floor + ',\n' +
    '     "status": "proposed",\n' +
    '     "dateAdded": "' + currentJSONDate + '"\n' +
    '   }\n</pre>';

  document.getElementById('customMarkerJSONDiv').innerHTML = sJSON + '<input type="button" value="Start Over" onclick="clearWindows();" />';


}

function GeoLocationBrowser() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showGeoLocatedPosition);
  } else {
    alert('Geolocation is not supported by this browser.');
  }
}

function showGeoLocatedPosition(position) {
  document.getElementById('customMarkerLng').value = position.coords.longitude;
  document.getElementById('customMarkerLat').value = position.coords.latitude;
  addCustomMarker();
}

function clearWindows() {
  //$("div.customMarker").hide();
  $('div#customMarkerLocationDiv').hide();
  $('div#customMarkerJSONDiv').hide();

  $('div#customMarkerGeoCodeDiv').show();
  $('div#customMarkerAddress').show();

  document.getElementById('customMarkerLng').value = -79.397593;
  document.getElementById('customMarkerLat').value = 43.678136;

  document.getElementById('customMarkerFloor').value = '';
  document.getElementById('customMarkerDirection').value = '';
}

//Option Window Code
function ShowAdvanced(what) {
  if (what.innerHTML=='+Show Advanced') {
    $('div#customAdvacned').show();	
    what.innerHTML='-Hide Advanced';
  } else {
    $('div#customAdvacned').hide();
    what.innerHTML='+Show Advanced';
  }
}

//Expand Option Window For Mobile
function optionExpand() {
  if ($('#mapOptions').hasClass('FullHeight')) { 
    $('#mapOptions').removeClass('FullHeight');
  } else {
    $('#mapOptions').addClass('FullHeight');
  }
}


google.maps.event.addDomListener(window, 'load', initialize);
<file_sep>/js/mapstyle.js
var mapStyle = [
  {
    "elementType": "geometry.fill",
    "stylers": [
      {
        "weight": 2
      }
    ]
  },
  {
    "elementType": "geometry.stroke",
    "stylers": [
      {
        "color": "#9c9c9c"
      }
    ]
  },
  {
    "elementType": "labels",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "elementType": "labels.text",
    "stylers": [
      {
        "color": "#120b2c"
      },
      {
        "visibility": "on"
      },
      {
        "weight": 0.5
      }
    ]
  },
  {
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#120b2c"
      }
    ]
  },
  {
    "elementType": "labels.text.stroke",
    "stylers": [
      {
        "color": "#ffffff"
      }
    ]
  },
  {
    "featureType": "administrative",
    "elementType": "geometry",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "administrative.neighborhood",
    "stylers": [
      {
        "color": "#ffffff"
      },
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "administrative.neighborhood",
    "elementType": "geometry.fill",
    "stylers": [
      {
        "color": "#00ffff"
      },
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "landscape",
    "stylers": [
      {
        "color": "#f2f2f2"
      }
    ]
  },
  {
    "featureType": "landscape",
    "elementType": "geometry.fill",
    "stylers": [
      {
        "color": "#fffbe9"
      }
    ]
  },
  {
    "featureType": "poi",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "road",
    "stylers": [
      {
        "saturation": -100
      },
      {
        "lightness": 45
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "geometry.fill",
    "stylers": [
      {
        "color": "#ec948b"
      },
      {
        "weight": 1
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "labels.icon",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#292343"
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "labels.text.stroke",
    "stylers": [
      {
        "color": "#fffbe9"
      },
      {
        "weight": 1.5
      }
    ]
  },
  {
    "featureType": "transit",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "transit.line",
    "elementType": "labels",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "transit.line",
    "elementType": "labels.text",
    "stylers": [
      {
        "weight": 0.5
      }
    ]
  },
  {
    "featureType": "water",
    "stylers": [
      {
        "color": "#46bcec"
      },
      {
        "visibility": "on"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "geometry.fill",
    "stylers": [
      {
        "color": "#9fd8d0"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#070707"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "labels.text.stroke",
    "stylers": [
      {
        "color": "#ffffff"
      }
    ]
  }
];
<file_sep>/news.md
---
title: News
layout: mkdownpage
---
# Ubiquiti Winter Test

#### 2017-12-16

The test was to place a short-term permanent link between two balconies through the cold winter season to test the resilience of the node and gather data.

## Deployment on 16 December 2017

Two Ubiquiti LBE-5AC-23 devices were placed on balconies with direct line-of-sight between them:


The distance between the two nodes is approximately **375 m**. At that distance the antennas are almost not visible to the naked eye. This made pointing the antennas a bit of a guessing game. A signal strength of about **-40 dBm** was established.

![Distance](/img/20171216_ubiquiti-winter-test3.png?raw=true)

## Node Construction

The radios were mounted on an ABS pipe stuck to a cinder block for support:

![Node One](/img/20171216_ubiquiti-winter-test4.png?raw=true)

![Node Two](/img/20171216_ubiquiti-winter-test5.png?raw=true)

Each Ubiquiti device had an Orange Pi Zero with a 5 GHz TOP-GS07 radio as a companion cjdns device. These devices were connected to the Ubiquiti device via ethernet cable. The Ubiqiti was configured as PTP with network set to Bridge mode.

On one end of the link, the Orange Pi Zero and PoE connector were placed inside the apartment with the flat ethernet cable sliding under the balcony weather stripping and door, while the Ubiquiti device was left outside.

One the other side of the link, the Orange Pi Zero was placed in a dollar store tupperware container plugged into outdoor power outlets. The cables came out the bottom of the container, and the container was taped shut with the opening angled downward.

![Container](/img/20171216_ubiquiti-winter-test6.png?)

The latter node meshed over WiFi and cjdns to another node located indoors. That indoor node in turn meshed with an Internet-enabled Atom computer with WiFi capabilities. This provided a path to Hyperborea and the Internet.

## Orange Pi Zero Node-to-node Speed Test

At signal strength of about -40 dBm, the following speeds were obtained from iperf3 tests:

### Clear IPv4

```
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-60.00  sec   661 MBytes  92.5 Mbits/sec  106             sender
[  4]   0.00-60.00  sec   661 MBytes  92.4 Mbits/sec                  receiver
```

### cjdns

```
 [ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  61.1 MBytes  51.3 Mbits/sec   94             sender
[  4]   0.00-10.00  sec  60.5 MBytes  50.7 Mbits/sec                  receiver
```

## Ubiquiti Radio-to-radio Speed Test

Tests between the pair of Ubiquiti devices alone showed speeds of **275 Mbps**. This is faster than tests involving the Orange Pi Zero's 10/100 ethernet and cjdns overheads. The speed measured here is consistent with results from earlier tests with the Ubiquiti devices, which are ~300 Mbps as well.

## Things Learned

* Both Ubiquiti antennas were set to the same LAN IP address of 192.168.1.20, which created an IP conflict on the bridged interface, so one was changed to 192.168.1.21

* A bad power brick for the Orange Pi Zero would prevent the WiFi and Mesh Point from working, and rebooting would bring it up for a very short period of time

* So far the outdoor node performed fairly well with outside temperatures dropping to -10 C, but node core temp never dropped below +8 C

* ABS tubing is not very riget and bend fairly easily, making the antenna sway slightly in windy conditions

* Wireless link through a balcony door and screen has a decent signal strength but horrible throughput

## Poor Performance Between Outdoor and Indoor Nodes

Very poor speeds were observed between an outdoor node with the first indoor node. Note that this occurs in the part of the network outside of the Ubiquiti devices, involving only the TOP-GS07 devices over Mesh Point:

```
[  4] local fc5e:44cd:6af4:4afc:cc4e:a75c:89a5:8d23 port 48622 connected to fc04:8b29:9889:cf83:88c:80fc:9e3b:78b4 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   244 KBytes  2.00 Mbits/sec   12   13.2 KBytes
....
[  4]  58.00-59.00  sec   125 KBytes  1.02 Mbits/sec    1   21.7 KBytes
[  4]  59.00-60.00  sec   250 KBytes  2.05 Mbits/sec    1   20.5 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-60.00  sec  9.12 MBytes  1.28 Mbits/sec  252             sender
[  4]   0.00-60.00  sec  8.98 MBytes  1.26 Mbits/sec                  receiver
```

The following steps were attempted to diagnose the issue:

* Replaced inside node with new node and TP-LINK TL-WN722N adapter (no improvement)
* Moved inside node outside (no improvement)
* Moved node inside (slight improvement to 3-4 Mbps)
* Removed top of tupperware container (no improvement)
* Signal strength during this whole time has been above -55 dBm
* Speed test against different node slightly farther registered 15 Mbps
* Suspecting of bad radio, replaced USB radio with new module (issue remains)
* Suspecting of bad power brick, replaced power brick (issue remains)
* Change one of the inside nodes to a "low-profile cased fanless" Orange Pi Zero with TP-LINK TL-WN722N WiFi adapter but kept the SD card to preserve settings (issue remains)

Notes:

* On 16 December 2017, patched a Node Exporter hack to collect Ubiquiti data from SNMP using the Orange Pi Zero nodes
* On 20 December 2017, moved the Orange Pi Zero node inside, link quality improved and metrics started recording correctly
* On 22 December 2017, further tests found that there may be an issue with the 3D-printed cases... when hardware is outside the case, speeds reach 30 Mbps, and placing it back in the case drops the speeds to 5 Mbps
* On 23 December 2017, moved one of the receivers down to below the balcony line so the signal is going _through_ the balcony glass, signal degraded minimally and speeds remained consistent





# Gigabit Link Field Test

#### 2016-10-15

## Test Setup & Objectives

A pair of Ubiquiti LiteBeam ac each powered by a ChargeTech 27 Ah portable battery through the PoE injector that the radios ship with. The LAN port of the injector is connected through an ethernet cable to an Ethernet-to-Thunderbolt gigabit adapter to a Mac. These two fully portable setups are largely symmetric. The LiteBeam ac's run AirOS and the Mac's run OS X with cjdns and other networking tools installed. On one of the ChargeTech batteries, we have attached a Kill-a-watt to measure the power draw of the LiteBeam ac units while idling and transmitting.

The objective of this exercise is to determine feasibility of using the LiteBeam ac to establish point-to-point, or perhaps point-to-multipoint, links for tomesh. We are primarily interested in the bandwidth and range performance of the unit, and secondary to that: power requirements, ease of set up, effects on obstructions by different objects in its line-of-sight path, and compatibility with cjdns.

On an early Saturday morning, the group met at a coffee shop near <NAME> Park, where there is a somewhat straight section of a few kilometres of flat land, which unfortunately does not have elevated points, but is a reasonable site for this first test. There we powered up the LiteBeams for the very first time.

## Initial Findings

- The Mac clients need to manually assign an IP to itself, with the router IP at its default **192.168.1.20**
- Connect with the AirOS web interface at **http://192.168.1.20**
- Switching one of the LiteBeams to **Router Mode** from the default **Bridge Mode** made it impossible to connect to again, this is probably due to our general infamiliarity with AirOS, but we had to reset that LiteBeam, and the reset button was damn hard to reach
- One LiteBeam was in **Station Mode** and the other in **Client Mode**, I don't recall if that was reverted and not reconfigured after the reset
- We changed one LiteBeam's IP to **192.168.1.19**, and once the link is established, only one of the two IPs become reachable from the Macs
- We initially had trouble having the Macs (each with a unique IP in the **192.168.1.0/24** subnet)  ping each other over the LiteBeam link, but eventually that started working after the resets and assigning different IPs to each LiteBeam
- Using `iperf3` while the LiteBeams were sitting right beside each other, the link was about 370 Mbps
- We expected cjdns running on the Mac to automatically bind to the point-to-point interface and automatically establish autopeering over the cjdns `ETHInterface`, but that didn't happen, neither did AirOS show cjdns' `tun0` as an interface
- We manually forced cjdns to bind to the point-to-point interface by changing `"bind": "all"` to `"bind": "x"`, where _x_ is `en3` on one Mac and `en5` which we found from `ifconfig`, after that the two Mac nodes autopeered over ethernet and we are able to ping each other through their cjdns addresses
- The cjdns link `iperf3` at only 20 Mbps, which remains a mystery because the underlying link is 370 Mbps, and I have seen these Macs do at least 50 Mbps through the `UDPInterface`, limited only by my home Internet speeds, and based on `cjdroute --bench` results I'd expect the Macs to saturate the raw link
- Since we are primarily interested in the LiteBeam performance over distance, this is when we moved on to <NAME>

## Range Testing

We had one group mount a LiteBeam at about 8 ft on a pole at (43.645771, -79.320951), while the other group moved away from it and stopped at various distances.

#### Test Point A, 227 m away at (43.643781,-79.321583)

```
$ ping 192.168.1.25
PING 192.168.1.25 (192.168.1.25): 56 data bytes
64 bytes from 192.168.1.25: icmp_seq=0 ttl=64 time=1.263 ms
64 bytes from 192.168.1.25: icmp_seq=1 ttl=64 time=1.363 ms
64 bytes from 192.168.1.25: icmp_seq=2 ttl=64 time=2.432 ms
64 bytes from 192.168.1.25: icmp_seq=3 ttl=64 time=2.972 ms
64 bytes from 192.168.1.25: icmp_seq=4 ttl=64 time=1.273 ms
64 bytes from 192.168.1.25: icmp_seq=5 ttl=64 time=1.322 ms
64 bytes from 192.168.1.25: icmp_seq=6 ttl=64 time=1.345 ms
64 bytes from 192.168.1.25: icmp_seq=7 ttl=64 time=2.797 ms
^C
--- 192.168.1.25 ping statistics ---
8 packets transmitted, 8 packets received, 0.0% packet loss
```

```
$ iperf3 -c 192.168.1.25
Connecting to host 192.168.1.25, port 5201
[  4] local 192.168.1.21 port 51076 connected to 192.168.1.25 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  45.2 MBytes   379 Mbits/sec
[  4]   1.00-2.00   sec  45.1 MBytes   378 Mbits/sec
[  4]   2.00-3.00   sec  42.4 MBytes   355 Mbits/sec
[  4]   3.00-4.00   sec  43.9 MBytes   368 Mbits/sec
[  4]   4.00-5.00   sec  41.2 MBytes   346 Mbits/sec
[  4]   5.00-6.00   sec  41.0 MBytes   343 Mbits/sec
[  4]   6.00-7.00   sec  44.6 MBytes   374 Mbits/sec
[  4]   7.00-8.00   sec  43.7 MBytes   366 Mbits/sec
[  4]   8.00-9.00   sec  44.3 MBytes   371 Mbits/sec
[  4]   9.00-10.00  sec  42.4 MBytes   356 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.00  sec   434 MBytes   364 Mbits/sec                  sender
[  4]   0.00-10.00  sec   434 MBytes   364 Mbits/sec                  receiver
```

**Obstruction one foot in front of one LiteBeam:**

```
$ iperf3 -c 192.168.1.25
Connecting to host 192.168.1.25, port 5201
[  4] local 192.168.1.21 port 51170 connected to 192.168.1.25 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  44.1 MBytes   370 Mbits/sec
[  4]   1.00-2.00   sec  39.4 MBytes   329 Mbits/sec
[  4]   2.00-3.00   sec  9.90 MBytes  83.4 Mbits/sec
[  4]   3.00-4.00   sec  37.0 MBytes   310 Mbits/sec
[  4]   4.00-5.00   sec  41.6 MBytes   349 Mbits/sec
[  4]   5.00-6.00   sec  34.1 MBytes   286 Mbits/sec
[  4]   6.00-7.01   sec  7.67 MBytes  64.0 Mbits/sec
[  4]   7.01-8.00   sec  29.3 MBytes   247 Mbits/sec
[  4]   8.00-9.00   sec  15.9 MBytes   133 Mbits/sec
[  4]   9.00-10.00  sec  34.9 MBytes   293 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.00  sec   294 MBytes   246 Mbits/sec                  sender
[  4]   0.00-10.00  sec   293 MBytes   246 Mbits/sec                  receiver
```

**Over cjdns:**

```
$ iperf3 -c fcfd:cce:14ab:57de:64f7:32e3:19f3:ebdf
Connecting to host fcfd:cce:14ab:57de:64f7:32e3:19f3:ebdf, port 5201
[  4] local fcaf:c9e1:bfff:73a3:c08c:51aa:3711:2ccc port 51316 connected to fcfd:cce:14ab:57de:64f7:32e3:19f3:ebdf port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.01   sec  2.49 MBytes  20.8 Mbits/sec
[  4]   1.01-2.00   sec  2.04 MBytes  17.2 Mbits/sec
[  4]   2.00-3.00   sec  2.68 MBytes  22.5 Mbits/sec
[  4]   3.00-4.01   sec  1.41 MBytes  11.8 Mbits/sec
[  4]   4.01-5.00   sec  2.58 MBytes  21.8 Mbits/sec
[  4]   5.00-6.00   sec  3.12 MBytes  26.2 Mbits/sec
[  4]   6.00-7.00   sec  3.06 MBytes  25.7 Mbits/sec
[  4]   7.00-8.00   sec  3.01 MBytes  25.3 Mbits/sec
[  4]   8.00-9.00   sec  3.10 MBytes  26.0 Mbits/sec
[  4]   9.00-10.00  sec  3.37 MBytes  28.3 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.00  sec  26.9 MBytes  22.5 Mbits/sec                  sender
[  4]   0.00-10.00  sec  26.7 MBytes  22.4 Mbits/sec                  receiver
```

**Notes:**

- If we introduced a misalignment of about 20 degrees, speed drops to < 100 mbps
- There were points when we had to restart `cjdroute` for things to work

#### Test Point B, 596 m away at (43.641396, -79.322121)

```
$ iperf3 -c 192.168.1.22
Connecting to host 192.168.1.22, port 5201
[  4] local 192.168.1.21 port 53527 connected to 192.168.1.22 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  43.5 MBytes   365 Mbits/sec
[  4]   1.00-2.00   sec  47.2 MBytes   395 Mbits/sec
[  4]   2.00-3.00   sec  45.3 MBytes   380 Mbits/sec
[  4]   3.00-4.00   sec  45.8 MBytes   384 Mbits/sec
[  4]   4.00-5.00   sec  44.5 MBytes   373 Mbits/sec
[  4]   5.00-6.00   sec  45.3 MBytes   380 Mbits/sec
[  4]   6.00-7.00   sec  47.6 MBytes   399 Mbits/sec
[  4]   7.00-8.00   sec  47.5 MBytes   399 Mbits/sec
[  4]   8.00-9.00   sec  45.4 MBytes   381 Mbits/sec
[  4]   9.00-10.00  sec  45.9 MBytes   385 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.00  sec   458 MBytes   384 Mbits/sec                  sender
[  4]   0.00-10.00  sec   457 MBytes   384 Mbits/sec                  receiver
```

**180 degree offset (lol wut? bc we can):**

```
$ iperf3 -c 192.168.1.22
Connecting to host 192.168.1.22, port 5201
[  4] local 192.168.1.21 port 53533 connected to 192.168.1.22 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  9.66 MBytes  81.0 Mbits/sec
[  4]   1.00-2.00   sec  10.9 MBytes  91.9 Mbits/sec
[  4]   2.00-3.00   sec  9.76 MBytes  81.9 Mbits/sec
[  4]   3.00-4.00   sec  8.12 MBytes  68.2 Mbits/sec
[  4]   4.00-5.00   sec  7.92 MBytes  66.4 Mbits/sec
[  4]   5.00-6.00   sec  8.79 MBytes  73.8 Mbits/sec
[  4]   6.00-7.01   sec  5.63 MBytes  47.0 Mbits/sec
[  4]   7.01-8.00   sec  8.05 MBytes  67.9 Mbits/sec
[  4]   8.00-9.00   sec  7.90 MBytes  66.1 Mbits/sec
[  4]   9.00-10.00  sec  4.96 MBytes  41.7 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.00  sec  81.7 MBytes  68.6 Mbits/sec                  sender
[  4]   0.00-10.00  sec  81.7 MBytes  68.6 Mbits/sec                  receiver
```

#### Test Point C, 863 m away at (43.638118, -79.322729)

- No line-of-sight, blocked by a bunch of trees, Fresnel zone probably cuts deep into the ground because the antennas are at ground level
- A few kbps intermittenly, signal dropping all the time
- Tried changing polarization but that made no difference
- AirOS web interface on one site does not load, consistent with our belief that once linked, only one LiteBeam is routing while the other one is just bridging
<file_sep>/_posts/2016-04-06-civic-tech-meetup2.md
---
title: Another Civic Tech Meetup
text: Join us at Civic Tech Toronto's weekly Tuesday meetup.
location: Civic Tech TO, Connected Lab, 370 King St W
locationLink: http://osm.org/go/ZX6Bjwlll?m=
attendees: 8
date: 2016-04-06
startTime: '20:10'
endTime: '21:00'
---

Join Toronto Mesh at [Civic Tech Toronto](http://civictech.ca) on Tuesday at 6:30pm.

A recap of what we discussed is available in the [{{ page.date | date: '%B %d, %Y' }} meeting notes](https://github.com/tomeshnet/documents/blob/master/meeting_notes/{{ page.date | date: "%Y%m%d" }}_meeting-notes.md).
<file_sep>/about.md
---
layout: mkdownpage
title: About
descriptiveTitle: About Toronto Mesh
order: 1
permalink: about
---

Based in Toronto, we are a grassroots and decentralized group of volunteers who started Toronto Mesh at [CivicTechTO](http://civictech.ca) in early 2016.  Through building community-owned infrastructure using off-the-shelf hardware and open-source technology, we are hoping to address barriers to internet access in our city. There are many ways that people can [be involved]({{ site.baseurl }}/events).

## Our Efforts

Since January 2016, Toronto Mesh has been working to build a community network using peer-to-peer, decentralized mesh network technology.
A mesh network consists of routers (or ‘nodes’) spread throughout the city with no single internet service provider or central servers. All nodes cooperate in the distribution of data, serving as a separate and redundant network (description via [NYCmesh](https://nycmesh.net/)).

## Vision

The primary driver for Toronto Mesh is to empower people to create spaces in which they can make decisions about the way they access and share information. For many of us, managing our online privacy means compromising it; the apps we download, the websites we visit, the operating systems we use, and even the infrastructure which carries our data around the world are implicated and leveraged by practices of surveillance for profit and social control. Internet access in general is sold in a 'black box'; meaning that the buyer doesn't know what it does or how it works. We believe this approach is at odds with the understanding of the internet as a place where people are empowered to act. Community-owned networks ask users to understand the technologies they use and to make decisions based on their community needs, rather than those of corporations.

As citizens, we cannot simply create our own parallel internet by pulling fibre optic cabling through the ground and the sea, or building data centres and switching stations. What we do have access to are the tools and devices through which countless wireless networks are already substantiated. In our project we are using familiar routers and antennas, we draw on lessons from existing projects worldwide in order to help us bring together residents, business owners, technologists, and others, to provide internet access in their neighbourhoods and cities. Building a mesh will be another way to connect people and spaces in this city to each other.

## Contact

Twitter: [@tomeshnet](https://twitter.com/tomeshnet)

Riot: [#tomesh:tomesh.net](https://chat.tomesh.net/#/room/#tomesh:tomesh.net)

Email: [<EMAIL>](mailto:<EMAIL>)

Github: [tomeshnet](https://github.com/tomeshnet)
