Random Developments And Random Toys

Extending ActiveRecord associations

Ever found yourself having difficulty to decide if method belongs to parent or child model?

Let’s say we have a Blogger and Article models. And profile section of our “blogging platform” displays list of articles published this current month.

Our option 1 is doing it in Blogger model:

class Blogger < ApplicationRecord
  has_many :articles

  def articles_this_month
    articles.where('created_at > ?', Time.current.beginning_of_month)
  end
end

And somewhere in our controller:

@articles = blogger.articles_this_month

Looks fine, but with domain models getting more complicated we may find our Blogger model polluted by articles_this, articles_that methods.

Option 2 is implement it in Article model using class method (or alternatively scope):

class Article < ApplicationRecord
  belongs_to :blogger

  def self.this_month
    where('created_at > ?', Time.current.beginning_of_month)
  end
end

And somewhere in our controller:

@articles = blogger.articles.this_month

Again, nothing wrong with that. Except maybe the fact that Article.this_month does not make too much sense on its own. In a way it pollutes Article model api with methods which don’t make sense without context of parent Blogger.

It’s almost as if our this_month method does not fit on both of our models and belongs to the relationship between them. Here we come to a somewhat little known ActiveRecord feature and our option 3: extending relationship between the models with custom methods.

has_many relationship documentation describes option :extend:

Specifies a module or array of modules that will be extended into the association object returned. Useful for defining methods on associations, especially when they should be shared between multiple association objects.

Our module then:

module BloggerArticles
  def this_month
    where('created_at > ?', Time.current.beginning_of_month)
  end
end

And the Blogger model:

class Blogger < ApplicationRecord
  has_many :articles, extend: BloggerArticles
end

Code somewhere in controller:

@articles = blogger.articles.this_month

Now neither Blogger nor Article has any out of place “this month” filter related methods. Instead, filter is now part of relationship between the models.

So, to summarise: models are not the only places to put your domain code. Relationship can also be a good option for that. And when used well - can be great way to build natural and well scoped domain APIs.

Adding delays to reduce ESP8266 power consumption

I’m currently working on a small ESP8266 gadget which runs UDP server and does some actions based on packets received.

Main loop goes a bit like this (note: I’m using Arduino core):

void loop() {
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
}

Looks fine until you remember that ESP8266 is a 32bit micro running at pretty whopping (for $1 price) 80Mhz frequency. So what does it actually do when there’s no packets coming in?

To start with, let’s figure out how many loops it does per second. This can be done by adding a counter and getting frequency by dividing it from time spent once in a while. Or.. If you have a scope, by flicking GPIO pin and measuring it:

void loop() {
    digitalWrite(5, HIGH);
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
    digitalWrite(5, LOW);
}

Result:

Loop frequency

107 thousand times / second! When with my app I can barely expect tens of packets / second. Which pretty much means that we’re burning CPU cycles needlessly.

Let’s add some delays (4ms in total) then and see what happens:

void loop() {
    digitalWrite(5, HIGH);
    delay(2);
    size_t size = udp.parsePacket();
    if (size > 0) {
        // handle the packet
    }
    digitalWrite(5, LOW);
    delay(2);
}

Result:

Loop frequency with some delays

Loop now runs at little more reasonable ~250 times / second. Should deal with tens of packets / second without human noticeable delay.

And the best part of that is power consumption change (logged by R&S HMC8043 power supply):

Current consumption logged by HMC8043

Without delays - constant almost 80mA current consumption. With delays - baseline 20mA current with spikes during modem wake ups.

Project details with Jupyter notebook to generate the plot here. And a very useful Espressif board post about esp8266 power modes.

Aisler - new KiCad friendly PCB prototyping service

So I’ve been raving about OSH Park and how good they are for about a year now. However there’s one tiny issue with them - cheapest service turnaround (from upload to PCBs arriving by post) is roughly 3 weeks if you live in EU.

From this year’s FOSDEM “KiCad project status” talk I learned that there’s new player in this - AISLER. They support manufacturing PCBs directly from KiCad’s ‘.kicad_pcb’ files, specs and are similar, turnaround is ~2 weeks if you’re in EU (guys are based in Germany).

Already got one small PCB from them - ESP8266 based RGBW strip controller. Smooth process, great quality and 2 week turnaround (which will let me to iterate on my hobbyist projects quicker).

KiCad render

PCB from AISLER

Assembled board with a genuine bodge

(Not so) interesting I2C PWM RGB led driver NCP5623

I was looking to something that could offload doing PWM from microcontroller + save precious I/O pins and found NCP5623:

Triple Output I2C Controlled RGB LED Driver

The NCP5623 mixed analog circuit is a triple output LED driver dedicated to the RGB illumination or backlight LCD display.

Sounds great, by my TL/DR of the datasheet would be: 5 bit PWM resolution is not enough if you plan doing artsy slow dimming.

Learned the hard way by actually getting parts + TSSOP−14 breakout to build test circuit. Slow fading is visibly choppy, 32 levels of brightness is just not enough.

But still, it’s an interesting chip.

First board from Oshpark

Expectation (the amazing KiCad 3D preview):

First board visual

Reality:

First board result