Python: Creating a singleton (to control an Arduino)

A while ago I was writing a tool in python to communicate with a sensor on an Arduino. The idea was to have a class for the Arduino that would hold the connection and handle the reading and writing to the serial port. However that way I needed to have the object maintain the connection and the open COM port (this was on windows), but this meant that I could not have multiple objects. Why? Because you can only have one connection to a COM port. The solution – use a Singleton design pattern.

What is a singleton?

Basically it is a class, that has only one object. No matter how many times you call the class you will get _the exact same_ object back. Not a copy, not one that looks like it, the exact same object. There is only one object for that class.

Creating a Singleton in Python

So how do we create a singleton in python? There are a couple of ways of doing this in python, but here’s what I did:

First let’s look at the simple Arduino class. We’ll just give it a single function ‘connect’ that sets the serial port.

class Arduino(object):
    ''' Class to handle communication with the Arduino. '''
    def __init__(self):
        self.com_port = ""
        self.ser = None

    def connect(self, serialport="COM8"):
        ''' Connect to the serial port arduino and read off the first two
        self.com_port = serialport
        self.ser = serial.Serial(self.com_port, 9600)

Then when we create Arduino objects we will get, as expected, different objects:

>>> Arduino()
<__main__.Arduino object at 0x7fccc5f79048>
>>> Arduino()
<__main__.Arduino object at 0x7fccc5f79080>

Next let’s create a Singleton class:

class Singleton(object):
    ''' Singleton class '''
    def __init__(self, klass):
        ''' Initiator '''
        # When we load the module and decorated classes call this __init__()
        # setting self.klass to the decorated class
        self.klass = klass
        self.instance = None

    def __call__(self, *args, **kwds):
        ''' When called as a function return our singleton instance. '''
        # When a new object is created of the decorated class __call__()
        # is called. We check if we already have an instance and if we do
        # we return it.
        if self.instance is None:
            self.instance = self.klass(*args, **kwds)
            # The above line is actually what calls Arduino.__init__
            # So only gets called the first time we create and Arduino
            # instance.
        return self.instance

To enable this decorator we uncomment the “#@Singleton” in the snippet above.

When the code is ran the python interpreter comes across the decorator and run’s the Singleton.__init__() method setting self.klass to the Arduino class.

When we create an Arduino object, the Singleton.__call__() function is called and this checks if self.instance is None, and it will be the first time, and then sets self.instance to self.klass (ie assigns it to an instance of Arduino)

Now if we try and create two different Arduino instances we find they are both the same:

>>> Arduino()
<__main__.Arduino object at 0x7fccc5f790f0>
>>> Arduino()
<__main__.Arduino object at 0x7fccc5f790f0>

The effect of this is that in our code we can create multiple Arduino instances if needed but they will in reality be the same instance that manages the serial connection.

Finally this does put extra responsibility on your code to manage what’s being sent and received down that serial line. If you have two threads running, potentially writing to the serial port at the same time you will get garbage. Some form of semaphore or mutex lock would be needed. But that’s a topic for another day.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top