Set Pi Time

Introduction

I recently had a work move and wanted to make something of an impression. My ambition to be, both, productive and cool has led me to the relatively simple project of placing a coffee cam in the workplace.

Ideally, I could attach a pi with a camera to the network and everyone could just bookmark the IP address. Unfortunately, my IT department is unlikely to approve this, so I am forced to keep my project offline. Using a wifi dongle, I am using the pi as a hotspot that anyone could log in to with their phone and use a browser to view the camera.

As a side-part of this project, I would like to keep a few basic stats. The pi - not having a real-time clock - is making this more difficult than I initially thought it would be.

How should I load time on a non-internet connected raspberry pi without having to log in on every reboot and set it?

Architecture

Though the purpose of this post isn't to document the coffee cam, it would help to have some of the architecture sketched out.

CoffeeCam Architecture

The pi hosts its own local wifi network to which local clients can connect.

Simple, right?

Solution

Client-Side

In speaking with a colleague, the solution naturally arose that each client that connects is likely to already have the local time. As it turns out, accessing the Date() object in javascript gives you 80% of what you need to set the time on your pi. Simply POST the time back to the web server, add a bit of parsing, and Bob's your uncle! I prefer to use jquery for DOM manipulation.

$(document).ready(
    function(){
        /* submit time using a post request */
        var date = new Date();
        $.post(
            '/set_time', 
            {
                'date': date.getTime(), 
                'timezoneOffset': date.getTimezoneOffset() * 60.0
            }
        );
    }
);

A couple of nuances to explain. The .getTime() method returns milliseconds since epoch. Completely unambiguous. The .getTimeZone() method returns the number of minutes offset from GMT. We multiply by 60 to get to seconds on the client.

Server-Side

The server is running an instance of Python Flask. We simply create a route to which to POST.

@app.route('/set_time', methods=['POST'])
def set_time():
    date = int(int(flask.request.form['date'])/1000)
    timezone_offset = int(flask.request.form['timezoneOffset'])

    dt = datetime.datetime.fromtimestamp(date)
    now = datetime.datetime.now()

    if dt < now + datetime.timedelta(hours=1) > dt or dt > now - datetime.timedelta(hours=1):
        logger.info('time already set, leaving it alone')
    else:
        logger.warning('setting time to {}'.format(dt))

        new_dt = dt.strftime('%m/%d/%y %H:%M:%S')
        os.system('hwclock --set --date="{}"'.format(new_dt))

    return '', 204

The one mild complication. If every client that connected has a slightly different time, then the local clock would be changed constantly. To keep this from happening, a reported time is compared to the local time and, if it is within an hour, then the time doesn't get set.

Final Thoughts

Cool solution, but it could use some refinement. If there is some guy who just won't keep the right time, the current solution could result in frequent time shifting. In the future, we may do some form of saving multiple times and having some level of voting to set the time, but this refinement may be a bit much for a coffeecam.



© by Jason R. Jones 2016
My thanks to the Pelican and Python Communities.