Looking inside the mind of a running MicroPython program

The text in red is just the REPL code in ThONNY getting upset…

I love typing in interactive commands to the MicroPython REPL console. It makes development much easier as I can test out program constructions and even view the contents of variables after I have interrupted the code. But the problem is that I lose the REPL console when the program starts running. MindReader fixes that. It lets you type in Python statements which will be executed as your program is running.

To use it you add the MindReader class to your program. Then make an instance of the class (I called it m) and call m.update() in your program at regular intervals. The update method scans the serial port and builds up a command. When you press return it executes the command. This means that you can still type in REPL commands when your code is running, which is neat. You can enter print statements to view variables or even modify them for testing. I’ve found it very useful. You can find the code on GitHub here.

Maketober Day 13: MicroPython JSON file save and load

Today finds me saving setting information for my nightlight. I want to save the time the “on” colour and the “off” colour along with the times when the light should change from one to the other. Like a good programmer I’ve made a class to hold the setting information:

blue = [0,0,255]
yellow = [255,255,0]

class Settings:

    file_name = 'settings.json'

    def __init__(self, on, off, on_colour, off_colour):
        self.on = on
        self.off = off
        self.on_colour = on_colour
        self.off_colour = off_colour

    @staticmethod
    def getDefault():
        return Settings([06,30],[18,30], yellow, blue)

This is the first part of my Settings class. The __init__ method initialises a setting instance. The getDefault method creates a light that comes on at 6:30 and goes off at 18:30. The light is blue when it is “off” and yellow when “on”. So far, so good. Now I want to store the value of a setting in a JSON file.

def save(self):
    settings_json = ujson.dumps(self.__dict__)
    print('Saving:', settings_json)
    try:
        f = open(Settings.file_name,'w')
        f.write(settings_json)
        f.close()
        return True
    except:
        return False

We can use the save method to ask a Setting instance to save itself. It converts the data dictionary in the Setting object into a string of JSON and then writes this string to a file. If all is well it returns true. If you’re wondering why I’m using ujson rather than json its because this is a special JSON library for MicroPython. So we can now save the setting values in a file. But can we get them back?

@staticmethod
def load():
    try:
        f = open(Settings.file_name,'r')
        settings_string=f.read()
        f.close()
        print('Got settings:', settings_string)
        settings_dict = ujson.loads(settings_string)
        result = Settings.getDefault()
        for setting in settings_dict:
            print("Setting:", setting)
            setattr(result,setting, settings_dict[setting])
        return result
    except:
        print('Settings file load failed')
        return Settings.getDefault()

This is the load method. It is a bit more fiddly then save. The problem is that when we load the JSON back from the stored file we get a dictionary, i.e. a set of name/value pairs. We don’t want a dictionary. We want a Setting object. So we have to build one which contains the loaded data. The first thing we do is create a “default” setting as a target. Then we work through the dictionary that we loaded from the file and use the magical setattr function to set each of the matching attributes in the result to the loaded value. If the loaded data is “sparse” (i.e. some of the attribute values are missing) the result that is created will have the default value for that missing attribute.

Note that I’ve made the load method static which means that it can be called from the Settings class. This means that I can load settings without needing to make a Settings instance to do it for me.

We could use these load and save methods to store a Settings object with any number of attributes. The only thing we can’t do is have Settings attributes which are themselves objects. The other wrinkle is that if you save a tuple attribute it will be loaded back as an array. But it does work and I rather like it.

Writing MicroPython using Visual Studio Code and Pymakr

I’m starting to really, really, like MicroPython. I particularly like the way that you can use the REPL command prompt to test out code before dropping it into your programs. I’ve been using an editor called Thonny which is nice enough but of course what I really want is to be able to use Visual Studio Code. It turns out that there’s a plugin for this. It’s called Pymakr. It lets you transfer Python files between your PC and your MicroPython device and provides a REPL prompt too. If you want to use it you have to install Node.js first. I did this and then found that it didn’t work. My MicroPython board was not detected.

I was using one of my Doit boards which usually works fine. (I’ve just bought another one….) It turns out that Pymakr maintains a list of USB devices that it is willing to connect with. You need to make sure that your device is on the list in the configuration file Pymakr.json. Use the command Pymakr>Global Setting to open this file and then add your device manufacture to the list, like I’ve done below.

Note that this is not the the manufacturer of your device, it is the manufacturer of the USB interface that the device uses to connect to the PC.

You can find the name that you need by opening Device Manager, right-clicking the com port where your board is connected and then getting the manufacturer name from the properties page:

I did this and everything started working. The Pymakr plugin has a nifty feature where it will copy all the Python files in your project into the device, or only copy the ones that you’ve changed. Very nifty.

If you’ve not played with MicroPython before I’ve written a tiny guide that you might find useful. You can find it here.