Tag: Projects

Chicken Cam: Incubator Edition

March 4, 2018 » Geek, Life

It’s been over a year since we’ve had chickens and we’ve missed them, so this Christmas we got Lizzy and Charlotte an incubator so that we could try hatching some this spring.

When we went to purchase eggs, we found that you could most easily get them 10 at a time from the hatchery we have used in the past, Murray McMurray. Since the incubator we got the girls could only hold seven, we would need something for the other three. Some searching found that you could use a styrofoam cooler and a lamp to create a makeshift incubator, so I planned on that.

Once I had a plan to create an incubator, I knew I would have to overcomplicate things. Four years ago I built a webcam for our chicks so I figured I would do that this time too. Also, just setting a lamp and thermometer in and hoping for the best seemed like a potential waste of good eggs, so I wanted to monitor the temperature and humidity, and regulate them.

My initial design was a Raspberry Pi connected to a cheap DHT11 temperature and humidity sensor, controlling a relay that could turn the light on and off. All of it would be hooked up through a PID controller to keep the temperatures right where we want them. Eventually, I added a thermocouple with a MAX6675 for more accurate temperature readings.

Raspberry Pi, Relay and a mess of wires.

The server side would be designed similarly to the previous chicken cam, except written in Go. The stats would be tracked in InfluxDB and Grafana would be used for viewing them.

After I got all the parts I did a little testing, then soldered things up and tested it to see how it ran.

Initially I wrote everything in Go, but the DHT11 reading was very spotty. Sometimes it would respond once every few seconds, and sometimes it would go a minute or more failing to read. I wired on a second DHT11 and tried reading from both, but I didn’t get that much better performance.

Eventually I tried them from the Adafruit Python library and had much better luck, so I decided to just read those from Python and send them to my main Go application for consumption. I still have trouble with the DHT11’s, but I suspect it’s my fault more than the sensors fault.

My next issue was that it was extremely jittery, the readings would vary by degrees one second to another, so I collected readings in batches of 5 seconds then averaged them. That smoothed it out enough that graphs looked reasonable.

On. Off. On. Off. On. Off.

Temperature was now well regulated, but the air wasn’t humid enough. I switched to a sponge and found I could manage it much easier that way. I briefly tried a 40W bulb thinking I could spend more time with the lamp off, but temperatures still plunged at the same rate when the light was off, so I mostly just created quicker cycles.

After putting the 25W bulb back in, I still wanted a longer, smoother cycle, so I wrapped up a brick (for cleanliness) and stuck that in there. That got me longer cycles with better recovery at the bottom, it didn’t get too cold before the lamp came back on. Some slight improvements to the seal of my lid helped as well. I had trouble with condensation and too much humidity, but some vent holes and better water management took care of that.

Before the brick.

After the brick.

For the server side, I mostly duplicated the code from the previous Chicken cam, but in Go. Then I used the InfluxDB library to get the most recent temperature and humidity readings for display.

At this point, I felt ready for the eggs, which was good because they had arrived! We placed them in the incubator and we’re just waiting now. On day 8 we candled them with a homebuilt lamp i.e. a cardboard box with a hole cut in it.


Things seem to be progressing well so far, so here’s hoping something hatches!


September 14, 2017 » Geek

When Rdio shut down, I tried a few services before landing on Google Play. It’s not perfect, but it’s good enough and it’s better than Spotify. One thing that seemed lacking was a desktop application, but that need was neatly filled by the excellent GPDMP.

One lesser known feature of GPDMP is the JSON API, which manifests as a simple JSON file that the application updates with information about the playback. When Slack announced custom statuses, I though back to the days of instant messaging and the integrations that set your status to the song you were playing.


Implementing the link from GPDMP to Slack was, in all, a fairly simple matter. First, I looked at the JSON file to get a feel for the structure.

    "playing": true,
    "song": {
        "title": "Freeze Me",
        "artist": "Death From Above 1979",
        "album": "Outrage! Is Now",
        "albumArt": "https://lh3.go...-e100"
    "rating": {
        "liked": false,
        "disliked": false
    "time": {
        "current": 363509,
        "total": 198000
    "songLyrics": null,
    "shuffle": "NO_SHUFFLE",
    "repeat": "NO_REPEAT",
    "volume": 100

Short and sweet! Now to represent that in Go for decoding.

type Song struct {
	Title    string
	Artist   string
	Album    string
	AlbumArt string

type PlaybackJSON struct {
	Playing bool
	Song    Song
	Rating  struct {
		Liked    bool
		Disliked bool
	Time struct {
		Current int
		Total   int
	SongLyrics string
	Shuffle    string
	Repeat     string
	Volume     int

I didn’t need to represent all the elements, but it’s a small structure so I went ahead with it. I didn’t embed Song because I wanted to write an equality test for that struct on it’s own. That will get used later on.

func (a Song) Equal(b Song) bool {
	return a.Title == b.Title && a.Artist == b.Artist && a.Album == b.Album

Next, I needed a way to monitor that file for updates, which GPDMP does fairly often. fsnotify was the obvious choice, and an easy drop in.
I added a time based debounce so that we don’t read the file on every update, which would be excessive. This will delay updates by up to whatever debounce is set to, but I’m okay with that trade off.

watcher, err := fsnotify.NewWatcher()
if err != nil {
defer watcher.Close()

go func() {
	var lastRead time.Time

	for {
		select {
		case event := <-watcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				if time.Now().After(lastRead.Add(debounce)) {
					lastRead = time.Now()
		case err := <-watcher.Errors:
			log.Println("error:", err)

err = watcher.Add(gp.Path)
if err != nil {

Inside that debounce (at line 16) we open the file, decode it to a new struct and, if it's playing, pass it off to a channel.

f, err := os.Open(event.Name)
if err != nil {

dec := json.NewDecoder(f)
pb := PlaybackJSON{}

err = dec.Decode(&pb)
if err != nil {

if pb.Playing {
	updates <- pb.Song

So, that's it for getting updates from GPDMP! Less than 100 lines, formatted. Now I needed to watch that update channel and post changes in status to Slack.

I found an excellent Slack API client on a different project, so I grabbed that. I started by building a little struct to hold my client and state.

type Slack struct {
	Client       *slack.Client
	CurrentSong  Song
	Set          bool
	InitialText  string
	InitialEmoji string

Then, during client initialization, we get the current custom status for the user and save it. This way, when you pause your music, it will revert to whatever you had set before.

func (s *Slack) Init() {
	auth, err := s.Client.AuthTest()
	if err != nil {

	user, err := s.Client.GetUserInfo(auth.UserID)
	if err != nil {

	s.InitialText = user.Profile.StatusText
	s.InitialEmoji = user.Profile.StatusEmoji
	log.Printf("Initial status: %s %s", s.InitialEmoji, s.InitialText)

Once it is initialized, we just need to range over our updates channel and post them to Slack when it changes. We set a timeout, because the GPDMP client won't send updates when the song is paused, or if the app quits updating the file (i.e. you quit GPDMP). By putting the logic for the timeout on this side, we have less to pass over the channel, and we can revert properly if something goes awry in the api reading goroutine.

func (s *Slack) Sync(emoji string, updates chan Song, revert_after time.Duration) {
	for {
		select {
		case song := <-updates:
			if !s.CurrentSong.Equal(song) {
				log.Printf("Sync: %s by %s\n", song.Title, song.Artist)
				s.Client.SetUserCustomStatus(fmt.Sprintf("%s by %s", song.Title, song.Artist), emoji)
				s.CurrentSong = song
				s.Set = true
		case <-time.After(revert_after):
			if s.Set {
				log.Printf("Reverting Status: %s %s\n", s.InitialEmoji, s.InitialText)
				s.Client.SetUserCustomStatus(s.InitialText, s.InitialEmoji)
				s.CurrentSong = Song{}
				s.Set = false

A little bit of glue in main and it's ready!

func main() {

	api := NewSlack(os.Getenv("SLACK_TOKEN"))
	gpdmp := &GPDMPAPI{os.Getenv("GPDMPAPI_PATH")}


	updates := make(chan Song)
	done := make(chan bool)

	go gpdmp.Watch(updates, done, 5*time.Second)
	go api.Sync(config.Emoji, updates, 15*time.Second)

You can browse the source and grab your copy at github.com/jmhobbs/gpdmp-to-slack

Xoket: The Learning Framework

February 16, 2012 » Geek

If you are new to it, frameworks are confusing, and just hard. Books and tutorials help, but there’s still a lot of “magic” going on in the background. This is good if you can treat it as a black box, but it’s better if you can find out what’s going on in there.

To this end, I have decided to write a framework, piece by piece, from scratch.

This framework does not have delusions of grandeur. I don’t expect anyone to actually use it in production, that is not what it’s for.

This framework is about learning and sharing. I’m going to blog my way through every component, as I build it, and keep a full history in github. I’m not going to lie, there will be mis-steps, but you’ll be able to see it all evolve commit by commit.


Principles are a good place to start – here are some for Xoket (zah-ket)

1. Documentation

As stated above, documentation is essential. PHPDoc formatting will be used.

2. MVC

Xoket will be a single request, MVC framework. HMVC is popular, but in the interest of complexity, we will not be implementing multiple request.

3. Explicit is better than Implicit

Taking a queue from The Zen of Python I will try to write explicitly, instead of using implicit magic. Unusual technique will be commented and discussed.

4. Modular

As much functionality that can be moved out of the core, should be kept out of the core. Auto-loading will be PSR-0 compatible, but also include some cascading functionality similar to Kohana.

5. Modern

Xoket will be designed for PHP 5.3 and higher. If you aren’t running modern PHP you should, there really isn’t a good excuse.

Getting Started

That’s the outline of Xoket – my next post will be about the early implementation of, well, whatever I decide to write first.

New Design!

February 5, 2012 » Geek, Life

After six years of sameness, I finally redesigned the blog.

While I was at it, I made it responsive, so it should look good on mobile too.

Responsive Layout

Some content is bound to still look funny as I haven’t reviewed all of it, but for the most part I think it looks good!

Special thanks to @johnhenrymuller for design help and my cool new logo.

Skunk: A stinky PHP microframework

November 18, 2011 » Geek

Six months ago I wrote a little PHP framework as an exercise. I’d been down this path before with the now abandoned Xoket – but this time I decided to go small, and throw away.

Hence Skunk was born.

Skunk is a single file, very lightweight API. Really, it’s just a borrowed router with some sugar wrapped around it.

I was after a Sinatra or Bottle feel, and I think I got something rather nice out of it.

Skunk uses anonymous functions extensively, so use PHP 5.3+ to save yourself the pain of create_function.


Here is a super simple Skunk application:

// Initialize
$s = new Skunk();

// Add a route
  function ( &$s, $name = null ) {
    $s->body = 'Hello' . ( ( is_null( $name ) ) ? '' : " $name" ) . '!';

// Run it!

Let’s tear that apart.

$s = new Skunk();

Everything in Skunk revolves around the Skunk object, so we need to set one up.

While it is possible to have multiple Skunk objects, there really isn’t a good use case I can think of. But we won’t restrict your cleverness with a singleton.


The two most important functions for Skunk are get and post.

These functions take a route and a function to apply when that route is matched, in a GET request and a POST request respectively.


In this chunk we are setting up a GET request, with the route /hi(/<name>), so that will match /hi, /hi/John, etc.

Note the identifier <name> in the route. This named match will be captured and sent as an argument to the function.

  function ( &$s, $name = null ) {
    $s->body = 'Hello' . ( ( is_null( $name ) ) ? '' : " $name" ) . '!';

Skunk route functions always need to take a reference to the Skunk object as their first argument. Following that are any other arguments that might be pulled from the route itself.

In this case we are just setting the body of the Skunk response.


This kicks off the request process and also renders the response.

Other Tricks

Skunk has some other features too.

You can raise a variety of errors inside of a request:

  function ( &$s ) {
    return $s->HTTP_500();

You can set headers:

  function ( &$s ) {
    $s->header( 'Content-Type', 'application/json' );
    $s->body = json_encode( array( "Example" => TRUE ) );

There is even a little hook system, so you can do middleware-ish stuff:

  function ( &$s ) { 
    $s->header( 'X-Stinky-By', 'Skunk' );


So that is Skunk.

It could use some love, but it’s workable. We’ve run Number Laundry on it with no problems for four months with no problems yet.

For an example of Skunk in action, check out Number Laundry’s source at github.