My name is Greg Winn and I am a Ruby developer, specializing in web application development. Take a look at some of my projects, then drop me a line if you want to talk!

Ruby I have been a software and electrical hardware engineer for over a decade. Getting my electrical engineering start with the National Association of Rocketry, building guidance and avoidance systems for high powered rockets. I have successfully launched many web platforms and SaaS based products. A United States Air Force veteran, I have also worked and contracted for some of the top companies and organizations.

Latest Updates

Environmentals using THINaer's IoT API

This article will assume you have read and understood the “Intro to THINaer’s API”. If you have not read that article it covers how to connect and authenticate with the API along with some organizational tips. This post will cover a simple use case using one Cirrus and a few iris (ii18e), iris 18e holds environmental sensors that monitor temperature and relative humidity. The chip used to monitor temperature is accurate to +/-0.5C and relative humidity to +/-2%. While the ii12 does display something called “internal temperature” it is very inaccurate and should only be used for non essential functions or if you want to take a close guess at temp.

iris from THINaer
Cirrus (IoT Gateway)

In the previous article I quickly talk about Cirrus and refer to it as an IoT gateway. Cirrus is simply a listener, it listens for BLE devices to transmit anything and then it sends that data through wifi to the THINaer IoT services to get processed and cleaned up for API usage. So simply said, it’s a “gateway to the internet” for our iris beacons.


Taking the Cirrus, I am going to plug it in and follow the setup instructions included in my box. This involves just a few steps:

  1. Provide power to the device.
  2. Using your computer with wifi, connect to the “Whisker.IO” wifi network.
  3. Scan for available wifi networks.
  4. Select and provide the password.

The Cirrus will power off then on and the blue LED should blink periodically but not with any uniformity. If it continues to blink with uniformity then just try again.

Now let’s place an ii18e indoors about 30 feet away from the Cirrus, and another outdoors about 50 to 80 feet away.

Once you have deployed the hardware it has already begun to collect data! Now that the Cirrus was powered on and connected to wifi it started sending data back to THINaer for use in the API.

Start collecting environmentals

Let’s do some basic organization of our hardware for later use, using POSTMAN or curl (or any programming language) lets place the two iris at a new “venue” but first let’s create a “venue” called “Home”. The API allows a developer to organize it’s devices based on “clients” and “venues”. This comes into play when you are building a multi tenant application using the API. You can then have many “clients” that belong to your application and assign devices to the client as you see fit, providing some separation for your customers. Within the “client” you can then further organize to “venues”, this often relates to physical locations but are really just groups and can be used anyway you see fit.

I will cover Clients and Venues and why you should be using them in a future post, but for now just know they are best practice.

Create a Client

So let’s first create a client to assign all my devices too called “Winn”, if I am building a reusable multi tenant product his would happen in a sign up process for my service.

curl --request POST \
  --url /api/v2/client/ \
  --header 'content-type: application/json' \
  --header 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVkIjoiMjAxNi0xMS0wMlQwMTo1NjowNS45NjVaIn0.i6KnWmSqzcXmLt5kxXbPpLfam9EkzXQwWaLRObHJ4lk' \
  --data '{\n	"name": "Winn"\n}'
Client Response
  "name": "Winn",
  "api_key": "ffed3a40-a753-11e6-8b7e-f524559cb962",
  "_id": "580504eb8c427a849136a3ee"
Create a Venue

Now we are going to use the id (580504eb8c427a849136a3ee) we got back to create and update the devices.

curl --request POST \
  --url http://api.thinaer.io/api/v2/venue/ \
  --header 'client-id: 580504eb8c427a849136a3ee' \
  --header 'content-type: application/json' \
  --header 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVkIjoiMjAxNi0xMS0wMlQwMTo1NjowNS45NjVaIn0.i6KnWmSqzcXmLt5kxXbPpLfam9EkzXQwWaLRObHJ4lk' \
  --data '{\n	"name": "Home"\n}'
Venue Response
  "__v": 0,
  "client_id": "580504eb8c427a849136a3ee",
  "name": "Home",
  "_id": "580833346fca85b59e347c1e"

Now lets use the new venue id to assign to the devices so they belong to that venue.

Assign each device to the newly created venue (580833346fca85b59e347c1e):

curl --request PUT \
  --url http://api.thinaer.io/api/v2/device/123456789a31 \
  --header 'client-id: 580504eb8c427a849136a3ee' \
  --header 'content-type: application/json' \
  --header 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVkIjoiMjAxNi0xMS0wMlQwMTo1NjowNS45NjVaIn0.i6KnWmSqzcXmLt5kxXbPpLfam9EkzXQwWaLRObHJ4lk' \
  --data '{\n	"venue_id": "580833346fca85b59e347c1e",\n}'

  # Do for each device.

Our simple application

Now we can write a simple script to collect the data every 15 minutes and save it to the database. In my example below I am using ruby from the THINaer API docs to look for all my devices by my venue_id and then looping them. Once looped and am saving them to redis for later use! At this point you can really do whatever you want to do with the information.

Get devices by venue_id.
require 'uri'
require 'net/http'

url = URI("/api/v2/venue/580833346fca85b59e347c1e/devices")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Get.new(url)
request["content-type"] = 'application/json'
request["client-id"] = '580504eb8c427a849136a3ee'
request["token"] = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVkIjoiMj...'

response = http.request(request)
devices = response.read_body
Loop the devices and save the data I want to redis.
devices.each do |device|
  REDIS.set(device['_id'] + "_temp", device['temp'])
  REDIS.set(device['_id'] + "_rh", device['rh'])
Front-end HTML view

The API provides the temp in celsius but on the front end I have converted it to fahrenheit.

iris from THINaer

My next article on the API will cover more in depth on organization and I will touch on movement history and how to see where your beacons have been!

Intro to THINaer's API

THINaer is an IoT based company that offers an API that provides proximity and environmental information based on deployed “iris” (AKA Beacons) and it’s “Cirrus” (an IoT Gateway). The iris devices are movable or static based on use case and the Cirrus is always static in a fixed location. iris transmits it’s signal at a set interval along with any other sensor data the iris may have, this can include; internal chip temperature on the “ii12”, external temperature and relative humidity on the “ii18e”, and finaly movement detection using an accelerometer on the “ii18a”.

iris from THINaer

The Hardware Technologie

THINaer is using the latest in BLE 4.1 technologie to transmit its packet over a custom protocol. This protocol can be picked up by any BLE device that can read device packets, tho because this is a custom proprietary protocol designed by the team and myself some development work will need to be done to decode the packets.

THINaer Protocol Breakdown

Each [ ] represents each byte in the packet and its position.

  • [0] 0x00 (Device Model)
  • [1] 0x5d (Internal Temp Channel)
  • [2] 0x00 (Internal Temp Value 1)
  • [3] 0xfa (Internal Temp Value 2)

Internal temp is represented as a 16-bit unsigned number in fixed point format. In the example above 0x00fa is 250 in decimal which represents 25.0C. To calculate temp you will first need to convert the hex to decimal then use: decimalValue * 0.1.

  • [0] 0x01 (Device Model)
  • [1] 0x44 (Temp Channel)
  • [2] 0x68 (Temp Value 1)
  • [3] 0xac (Temp Value 2)
  • [4] 0x45 (RH Channel)
  • [5] 0x49 (RH Value 1)
  • [6] 0xba (RH Value 2)

The ii18e has a more sensitive temperature chip on board and provides a much more accurate reading. And is still represented in two bites but the calculation changes. (175.72 * decimalValue) / 65536 - 46.85

  • [0] 0x02 (Device Model)
  • [1] 0x5d (Internal Temp Channel)
  • [2] 0x00 (Internal Temp Value 1)
  • [3] 0xfa (Internal Temp Value 2)
  • [4] 0x09 (Motion Channel)
  • [5] 0x00 (Motion Value)

The ii18a also have an internal temperature output but its primary focus is motion. Motion can be detected by watching the motion channel, anything greater than 0x00 is movement.


In just a few days the company will be releasing a new version of the API (v2), so everything that follows will talk about using the latest version. The THINaer API provides near-real time access to proximity and environmental information. It also provides historical movement information, this is used for asset tracking use cases, we will talk more about that later. The API also provides a two way look, meaning I can see what a Cirrus can see or I can look from the perspective of an iris.

Something special with this API is that it allows a developer to organize it’s devices based on “clients” and “venues”. This comes into play when you are building a multi tenant application using the API. You can then have many “clients” that belong to your application and assign devices to the client as you see fit, providing some separation for your customers. Within the “client” you can then further organize to “venues”, this often relates to physical locations but are really just groups and can be used anyway you see fit.

Getting started - Authentication

Before you can make any requests to get data, you need to authenticate with the API. Below is an example using CURL.

curl --request POST \
  --url /api/v2/application/token \
  --header 'content-type: application/json' \
  --header 'secret: cf2aca10-9355-11e6-8b95-7932558ded9d' \
  --header 'uid: 7345ccf0-fa43-4ad2-a5c0-d26a172c2acd'

The UID and Secret are provided by THINaer at first setup. This token request will return just that, a token that should be saved and will expire in 24 hours.

Let’s get the iris and Cirrus devices assigned to your account!

Now that you have authenticated lets looks the the devices assigned to your account. Using the token you just received we will request all devices by type. See below:

iris Devices by application

curl --request GET \
  --url /api/v2/application/iris \
  --header 'content-type: application/json' \
  --header 'token: 876tyui3ol387ui3o93ikejui893eoieku19oke0'

Cirrus Devices by application

curl --request GET \
  --url /api/v2/application/cirrus \
  --header 'content-type: application/json' \
  --header 'token: 876tyui3ol387ui3o93ikejui893eoieku19oke0'

The two objects returned will display all devices assigned to your account, you can save that data locally or make this request as often as you like. When you buy new devices they will always show under your account and this request will update the moment they are added.

Going forward

If you come back, I will cover some simple use cases and how to get the data out of THINaer! Now that you have a basic understanding of how to authenticate and make your first request using the new version 2 THINaer API going forward the requests will be much of the same style just with select ID’s sent in as headers or GET params.

MySQL 5.7 issues

After the latest Ubuntu 16.04 update, I did what thousands and thousands of people did. I upgraded from 15.10 while using and older version of MySQL (5.6).

Testing the update

The first thing I did was confirm that my Ruby projects still worked and all started up. I pulled the latest from my project and ran a rake db:migrate and it failed. I then tried to connect to my local database mysql -u root -p (My MySQL setup locally has NO password: this is important to remember, this issue ONLY effects those with no password set for root). While I was updating I also notice that MySQL was upgraded from 5.6 to 5.7.

“Fix” MySQL 5.7 to work like it use to.

First we need to connect to mysql and by now you have found that you can not… Here is how:

sudo mysql -u root -p
(enter your sudo password)
(no password for mysql)

Now we need to alter the User table to remove the new auth_socket plugin.

mysql > UPDATE mysql.user SET plugin = "mysql_native_password" where User = 'root' AND plugin = "auth_socket";
mysql> quit

Lets stop and start the service and connect like normal!

sudo service mysql stop
sudo service mysql start

mysql -u root
mysql >