Building an Arduino Time Tracker Cube with Toggl's Open API

Written by chingiz | Published 2021/10/28
Tech Story Tags: time-tracking | arduino | arduino-tutorial | esp8266 | mpu6050 | api | toggl | work-life-balance

TLDRvia the TL;DR App

Hours, days, weeks, and months pass but it will always feel like there isn’t enough time in the day. It seems that from morning to evening you work hard, but you don't see the result. To start using time as efficiently as possible, first need to understand what time is being spent on. As we know, what gets measured gets managed. Also, it is required to maintain a work-life balance so as not to burn out at work. After seeing reports on your time spent for a week or a month, you can draw conclusions and optimize your daily routine or understand which activities bring great results and concentrate on them.

Let's move on to how I tried to simplify this activity. Tracking time with just flipping the cube is such an attractive idea. All home time tracking projects I’ve seen are either uploading data to CSV or Excel files. The uniqueness of this project is that the cube is integrated with an already working time tracking system with its own web, desktop, and mobile versions so we can easily make changes to the records, start another time entry from a phone, or generate reports for specified time periods. Below are detailed instructions on how to assemble a timer cube based on a microcontroller.

All we need is an accelerometer sensor (MPU6050) and a wi-fi module. I am using NodeMCU but the same logic should work for Arduino-ESP8266. Here is a link to the project git page where you can find the code for this project. We will also walk through the code in more detail below.

Getting Cube Side Working With Accelerometer MPU6050

Connection

MPU6050 has an I2C interface which should be used for the connection. For Arduino UNO: A5 — SCL, A4 — SDA, 5V—VCC and GND — GND; for NodeMCU: D1— SCL, D2 — SDA, 3.3V— VCC and GND — GND; I have used NodeMCU in my case but the code is not limited within the board.

Cube

You can use any cube but to make the project easy I have printed the paper cube on paperboard:

When folded, place the sensor into it, and number the sides:

Getting raw data from the sensor

Here is the code that will give us accelerometer and gyroscope data. Upload the code, open the serial monitor, and set the baud rate to 9600.

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
MPU6050 mpu;
int16_t ax, ay, az;
int16_t gx, gy, gz;
void setup() {
Wire.begin();
Serial.begin(9600);
mpu.initialize();
//connection status
Serial.println(mpu.testConnection() ? "MPU6050 OK" : "MPU6050 FAIL");
delay(1000);
}
void loop() {
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
Serial.print(ax); Serial.print('\t');
Serial.print(ay); Serial.print('\t');
Serial.print(az); Serial.print('\t');
Serial.print(gx); Serial.print('\t');
Serial.print(gy); Serial.print('\t');
Serial.println(gz);
delay(50);
}

It will return values in the range between -32768 … 32767. To make working with the data easier, let’s make the range shorter(-100 … 100 ) by dividing the values by 327. Accelerometer data (ax, ay, az) will be used in our project. To get the side of the cube we will use the acceleration of the earth — gravity.

Serial.print(ax/327); Serial.print('\t');
Serial.print(ay/327); Serial.print('\t');
Serial.print(az/327); Serial.print('\t');
Serial.println("");
delay(50); 

Identifying the Cube Side

If we open the Serial Monitor which is running the last block of code, we can see that it gives two values. One around 0 and the other around 50/-50. This will be applicable for each side of the cube. ex: -1 -3 46.

If we rewrite values for each side we can see something like this:

_|X   |Y   |Z 
0|-1  |-3  |46 
1|50  |-5  |-1 
2|-2  |-50 |-8 
3|-50 |0   |-8 
4|2   |50  |0 
5|7   |3   |-50

I’ve created a sides array and it needs to be filled with numeration of the cube sides appropriately. This is done to make our further steps easier.

///////////////+X,-X,+Y,-Y,+Z,-Z
int sides[] = { 1, 3, 4, 2, 0, 5};

To check if cube is on position #1, we need to get if the ax var is ~50. This can be easily done by an if statement:

if(50-epsilon < ax && ax < 50+epsilon ){//40 < 50 < 60 
  Serial.print("Position #1"); 
}

This needs to be done for each side. To make our code more readable and organized, I have written a separate function for it :

int cube_side = -1;
int16_t epsilon = 10;
if(checkSide( 50, ax/327) ){ cube_side=sides[0]; }
else if(checkSide(-50, ax/327) ){ cube_side=sides[1]; }
else if(checkSide( 50, ay/327) ){ cube_side=sides[2]; }
else if(checkSide(-50, ay/327) ){ cube_side=sides[3]; }
else if(checkSide( 50, az/327) ){ cube_side=sides[4]; }
else if(checkSide(-50, az/327) ){ cube_side=sides[5]; }
Serial.print(cube_side); Serial.print('\t');

bool checkSide(int16_t side, int16_t a){ //return true or false
  if(side-epsilon < a && a < side+epsilon ){//40 < 50 < 60
    return true;
  }
  return false;
};

Checking Cube is in a Stable Position

Now, we can get the cube side but this is not enough to start time tracking. We need to check if the cube is in a stable position / not moving. To reach it, I have created an array that will store the last 60 measurements and we will check if all of them are the same. We will add a delay of 50 milliseconds between each measurement. Time tracking will be started after 3 seconds in a stable position.

//Initialization of the array int last_60_measurements[60] = {};

//Moving each measurement to one cell right
for (int i=0;i<59;i++){
last_60_measurements[i]=last_60_measurements[i+1];//0=1,1=2..
}

//And assign first one with new value
last_60_measurements[59] = cube_side;

if(checkIfCubeStable(last_60_measurements)){
Serial.print("Stable position"); Serial.print('\t');
}

//Checking if all 60 values are same
bool checkIfCubeStable(int measurements[]){
for (int i=0;i<60;i++){
if(i==59){return true;};
if(measurements[i] != measurements[i+1]){return false;};
}}

Also, we need to check if the already sent side data is not equal to the current position:

//Initialization of the sent side
int sent_cube_side = -1;

//Checking if last sent side and current side is not equal
if(sent_cube_side!=cube_side){
Serial.print("SEND DATA!"); Serial.print('\t');
//Update the sent side to new side
sent_cube_side = cube_side;
}

And finally, we come to the place where we need to start time tracking.

What is Toggl Track?

If you are interested in time tracking you should already have heard about the Toggl track. For others, this is an incredibly simple time tracking app with applications for iOS, Android, macOS, Windows, Linux, and web versions. But most importantly for us, it has open API documentation.

To make our time entries more organized here, we will use the projects feature.

Toggl track API

For working with Toggl track API the only thing you need to know is your email and password. The rest of the necessary data will be revealed below. Here are some commands to get information regarding your workspace, projects, and to test if the start time entry requests are working properly.

To do it, a command line with curl could be used. For macOS and Windows 10 (version 1803 or later) curl is installed by default, others could find an installation manual and do it or just use one of the online tools to run curl commands, for example: https://reqbin.com/curl. Certainly, these commands could be run in Postman by clicking ‘Import an existing file’ > ‘Raw text’. First command (replace email and password accordingly):

curl -v -u email:password -X GET 
https://api.track.toggl.com/api/v8/me

Response example:

{
  "since":1361780172,
  "data": {
    "id":123,
    "api_token":"1971800d4d82861d8f2c1651fea4d212",
    "default_wid":777,
    "email":"john.doe@gmail.com",
    "fullname":"John Doe",
    "jquery_timeofday_format":"h:i A",
    "jquery_date_format":"m/d/Y",
    "timeofday_format":"h:mm A",
    "date_format":"MM/DD/YYYY",
    "store_start_and_stop_time":true,
    "beginning_of_week":1,
    "language":"en_US",
    "duration_format": "improved",
    "image_url":"https://www.toggl.com/images/profile.png",
    "at": "2015-02-17T16:58:53+00:00",
    "created_at": "2014-07-31T07:51:17+00:00",
    "timezone": "Europe/London",
    "retention": 9,
    "new_blog_post":{},
    "projects": [
      {
        "id":90123,
        "wid":777,
        "name":"Our best project",
        "billable":true,
        "active":true,
        "at":"2013-02-12T09:47:57+00:00",
        "color":"5"
      }
    ],
    "tags": [
      {
        "id":238526,
        "wid":777,
        "name":"billed"
      }
    ],
    "tasks": [],
    "workspaces": [
      {
        "id":777,
        "name":"John's WS",
        "at":"2012-11-28T11:56:49+00:00",
        "default_hourly_rate": 0,
        "default_currency": "USD",
        "projects_billable_by_default": true,
        "rounding": 1,
        "rounding_minutes": 0,
        "api_token": "ea897..."
      }
    ],
    "clients": []
}

The important information here for us is default_wid or workspaces.id which will be used in our next request to get information regarding your projects. Replace WORKSPACE_ID with your one:

curl -v -u email:password 
-X GET https://api.track.toggl.com/api/v8/workspaces/WORKSPACE_ID/projects

In the response, you will find the id for each project. (You can also start time entry without any projects). Lastly, the project id could be used in the query to start time entry, the description could be updated accordingly:

curl -v -u email:password \
-H "Content-Type: application/json" \
-d '{"time_entry":{"description":"Description goes here","tags":[],"pid":PROJECT_ID,"created_with":"curl"}}' \
-X POST https://api.track.toggl.com/api/v8/time_entries/start

Here is the code example which will be run on Arduino to start time Entry:

POST /api/v8/time_entries/start HTTP/1.1 Host: api.track.toggl.com
Content-Type: application/json
Authorization: Basic Y2WG...lGsA==
Content-Length: 102
{"time_entry":{"description":"Description goes here","tags":[],"pid":PROJECT_ID,"created_with":"curl"}}

The Authorization code could be found in any response to our commands or in postman open ‘code snippet’ > HTTP and see the same code above.

Arduino HTTPS command run

Here is list of libraries we need:

#include <ESP8266WiFi.h> 
#include <WiFiClientSecure.h> 
#include <ESP8266WebServer.h> 
#include <ESP8266HTTPClient.h>

We will need to connect to wi-fi. Set network name and password:

const char *ssid = "wifi_name";    //Wifi Network Name
const char *password = "wifi_key"; //Wifi Network Key

Let’s set an authorization credential as separate variable:

const char *authorization = "Basic Y2WG...lGsA==";

We need to set trackers for each cube side. The first item is content length, the second is description and the third one is pid (project id):

const char *trackers[6][3] = 
{{"97",  "Email check",             "172635855"}, 
 {"93",  "Meeting",                 "172635927"}, 
 {"93",  "Reading",                 "172718034"}, 
 {"109", "Online training courses", "172718034"}, 
 {"84",  "Relax",                   "\"\""     }};

Some other variables which are need to be declared for future use:

const char *host = "api.track.toggl.com";
const int httpsPort = 443;

String datarx; //Received data as string
int httpsClientTimeout = 5000; //in millis

Initialization of the wi-fi connection (in setup):

WiFi.mode(WIFI_OFF);
delay(1000);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to wifi: ");
Serial.print(ssid);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.print("Connected to wifi: ");
Serial.println(ssid);

Below is the function that will be used to start time tracking. In the beginning, we are trying to make a connection to the server. Then, we send a POST request. After that, we get the response and print it.

void callhttps_start_time_entry(const char* content_length, const char* description, const char* pid){ 
  WiFiClientSecure httpsClient; 
  httpsClient.setInsecure(); 
  httpsClient.setTimeout(httpsClientTimeout); 
  delay(1000); 
  int retry = 0; 
  while ((!httpsClient.connect(host, httpsPort)) && (retry < 15)) { 
    delay(100); 
    Serial.print("."); 
    retry++; 
  } 
  if (retry == 15) {Serial.println("Connection failed");} 
  else {Serial.println("Connected to Server");}

  Serial.println("Request_start{");
  String req = String("POST /api/v8/time_entries/start HTTP/1.1\")
    + "Host: api.track.toggl.com\"
    +"Content-Type: application/json\"
    +"Authorization: " + authorization + "\"
    +"Content-Length: " + content_length + "\"
    +"{\"time_entry\":{\"description\":\"" + description + "\",\"tags\":[],\"pid\":" + pid + ",\"created_with\":\"time_cube\"}}" + "\r\n\r\n";

  Serial.println(req);
  httpsClient.print(req);
  Serial.println("}Request_end");

  Serial.println("line{");
  while (httpsClient.connected()) {
    String line = httpsClient.readStringUntil(');
    Serial.print(line);
    if (line == "\r") {
      break;
    }
  }
  Serial.println("}line");

  Serial.println("datarx_start{");
  while (httpsClient.available()) {
    datarx += httpsClient.readStringUntil(');
  }
  Serial.println(datarx);
  Serial.println("}datarx_end");
  datarx = "";
}

We can now call the function to start time tracking. Parameters are content length, description and project id of the time entry which was set in the trackers array before.

callhttps_start_time_entry( trackers[sent_cube_side][0],
trackers[sent_cube_side][1], trackers[sent_cube_side][2]);

Conclusion

In the end, I would like to share with you what changes time tracking has brought to my life.

Since I started tracking my time, I have become more careful with my time. I spend it wisely and maintain a healthy work-life balance. For many, the idea of time tracking is unattractive due to the fact that time tracking is also a job. Many say that over time it becomes automated and will require much less effort than in the beginning. But in my case, it didn't work and I decided to make this process easier. Time tracker cube helped me keep track of my time simply and easily.

Also, it is easy to edit or see the report in the Toggl track. It was really interesting, but the most important thing is that this is really a working project for daily use. After finishing the project, I was not sure whether it was worth writing and publishing this article, but I saw that I spent over 50 hours of my free time on this home project and decided that this work would be useful for someone at least to save this time. This is one of the advantages of tracking your time. You begin to appreciate your work and time.

Thank you for your time and I hope you spend your time only on what is important for you. I plan to implement new features in the future that will ease the process of time tracking even more.

So keep tuned.


Written by chingiz | Love Robotics and IoT
Published by HackerNoon on 2021/10/28