Posted on Wed 13 April 2016

Standing Automation

I've had a standing desk at work for a long time, but I've always felt that I wasn't using it enough, that I still spent most of my timing sitting.

This week I finally decided to do something about it.

LV-MAXSONAR-EZ1

I bought a small sonar sensor (LV-MaxSonar-EZ1), hooked it up to an Arduino and added a little Python script to sample the sensor once a minute.

The sonar is mounted to my table and measures the distance to the floor, and with ~3 cm accuracy it's very easy to distinguish whether I'm sitting or standing.

Since the Sonar happily runs of 5V, I just connected its power input pins directly to the VCC and GND pins of my Arduino. Conveniently it also provides the measured distance as analog voltage on a pin, I just connected that to A0 on the Arduino.

The Arduino code to regularly sample the sonar is then very simple, even with basic smoothing included:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void setup() {
  Serial.begin(9600);
}

const float sample_duration = 500;

void loop() {
  // Protect against timer overflow.
  unsigned long start = millis();
  const unsigned long deadline = start + sample_duration;
  double cumulative_sensor_value = 0;
  unsigned long steps = 0;

  while (millis() < deadline && millis() >= start) {
    cumulative_sensor_value += analogRead(A0);
    steps++;
  }

  // Sonar values are (Vcc/512) per inch, Arduino sensor range is [0, 1023].
  float distance_inch = (cumulative_sensor_value / steps) / 2.0;
  float distance_m = distance_inch * 2.54 / 100;
  Serial.println(distance_m);
}

The Arduino includes a USB serial interface, so I can just connect it directly to my computer and it's straightforward to read its output with Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import serial
import sys
import time

desk_height = None
deadline = time.time() + 30

with serial.Serial(port='/dev/ttyUSB0', timeout=1) as ser:

  while desk_height is None:
    line = ser.readline().strip()
    if line:
      try:
        desk_height = float(line)
      except ValueError:
        pass

    if time.time() > deadline:
      print "failed to read desk height"
      sys.exit(1)

print 'standing' if desk_height > 0.5 else 'sitting'

At this point I can do whatever I want with the data - create daily or weekly graphs, send automated email reminders if I haven't been standing in the last 2 hours, etc.

To ignore periods where I'm not actually at my computer, I use xprintidle, which prints the time in milliseconds since the last user activity, i.e. mouse or keyboard input.

Tags: misc

© Julian Schrittwieser. Built using Pelican. Theme by Giulio Fidente on github. .