{"id":13802054,"url":"https://github.com/peterhinch/micropython-msgpack","last_synced_at":"2025-04-30T15:31:27.144Z","repository":{"id":46314388,"uuid":"389690118","full_name":"peterhinch/micropython-msgpack","owner":"peterhinch","description":"MessagePack serialisation library optimised for MicroPython","archived":false,"fork":false,"pushed_at":"2024-08-07T09:33:31.000Z","size":70,"stargazers_count":27,"open_issues_count":0,"forks_count":12,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-08-08T11:42:52.394Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterhinch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-26T15:58:22.000Z","updated_at":"2024-08-07T09:33:35.000Z","dependencies_parsed_at":"2024-08-07T11:34:47.182Z","dependency_job_id":null,"html_url":"https://github.com/peterhinch/micropython-msgpack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-msgpack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-msgpack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-msgpack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterhinch%2Fmicropython-msgpack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterhinch","download_url":"https://codeload.github.com/peterhinch/micropython-msgpack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224215504,"owners_count":17274798,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:01:34.436Z","updated_at":"2024-11-12T04:21:08.644Z","avatar_url":"https://github.com/peterhinch.png","language":"Python","readme":"# 1. MicroPython MessagePack Introduction\n\n[MessagePack](http://msgpack.org/) is a serialization protocol similar to JSON.\nWhere JSON produces human readable strings, MessagePack produces binary `bytes`\ndata. The protocol achieves a substantial reduction in data volume. Its\ncombination of ease of use, extensibility, and packing performance makes it\nworth considering in any application where data volume is an issue.\n\nThis MicroPython implementation has usage identical to that of the `ujson`\nlibrary:\n```python\nimport umsgpack\nobj = [1, 2, 3.14159]\ns = umsgpack.dumps(obj)  # s is a bytes object\n```\nMessagePack efficiently serialises a range of Python built-in types, a range\nthat can readily be extended to support further Python types or instances of\nuser defined classes. A key feature of the protocol is that, like JSON, it is\nself describing: the receiver gets all the information needed to decode the\nstream from the stream itself. This example assumes a file whose contents were\nencoded with MessagePack.\n```python\nimport umsgpack\nwith open('data', 'rb') as f:\n    z = umsgpack.load(f)\nprint(z)  # z might be a wide range of Python objects\n```\nThe protocol is self describing even when extended. The file\n`umsgpack/umsgpack_ext.py` extends support to `complex`, `tuple` and `set`\nbuilt-in types. Provided that file exists on the target, the code above will\nwork even if the data includes such objects. Extension can be provided in a way\nthat is transparent to the application.\n\nThis document focuses on usage with MicroPython and skips most of the detail on\nhow MessagePack works. The\n[source document](https://github.com/vsergeev/u-msgpack-python) provides a lot\nof useful information for those wishing to understand the protocol. Also see\n[the MessagePack spec](https://github.com/msgpack/msgpack/blob/master/spec.md).\n\n## 1.1 Supported types\n\nThe following types are natively supported.\n * `int` In range `-2**63` to `2**64 - 1`.\n * `True`, `False` and `None`.\n * `str` Unicode string.\n * `bytes` Binary data.\n * `float` IEEE-754 single or double precision controlled by a dump option.\n * `tuple` By default tuples are de-serialised as lists, but this can be\n overridden by a load option. The extension module provides proper support\n wherby `list` and `tuple` objects unpack unchanged.\n * `list` Termed \"array\" in MessagePack documentation.\n * `dict` Termed \"map\" in MessagePack docs.\n\nThe `list` and `dict` types may contain any supported type including `list` and\n`dict` instances (ad nauseam).\n\nThe file `umsgpack_ext.py` (in `umsgpack` directory) extends this to add the\nfollowing:\n * `tuple` Provides an explicit distinction between `tuple` and `list` types:\n if it is encoded as a `tuple` it will be decoded as one.\n * `complex`\n * `set`\n\n\n\n## 1.2 Performance\n\nThe degree of compression compared to UJSON depends on the object being\nserialised. The following gives an example:\n```python\nimport umsgpack\nimport ujson\nobj = [1, True, False, 0xffffffff, {u\"foo\": b\"\\x80\\x01\\x02\", u\"bar\": [1,2,3, {u\"a\": [1,2,3,{}]}]}, -1, 2.12345]\nlj = len(ujson.dumps(obj))\nlm = len(umsgpack.dumps(obj, force_float_precision = \"single\"))\nprint(lj, lm, lj/lm)\n```\nOutcome: `lj` == 106 bytes, `lm` == 41 bytes corresponding to a transmission\nspeedup factor of 2.6. The `force_float_precision` option ensures the same\nresult on 32 bit and 64 bit platforms.\n\nIf large quantities of text are to be transmitted, a greater gain could be\nachieved by compressing the text with a `gzip` style compressor and serialising\nthe resultant `bytes` object with MessagePack.\n\n# 2. The MicroPython implementation\n\nThis implementation is based on the following well proven MessagePack repo\n[u-msgpack-python](https://github.com/vsergeev/u-msgpack-python). This runs\nunder Python 2 and Python 3. With trivial adaptations it will run under\n[MicroPython](https://micropython.org/) but at a high cost in RAM consumption.\nThis version was adapted from that codebase and optimised to minimise RAM usage\nwhen run on microcontrollers. Consumption is about 12KiB measured on STM32.\nUsing frozen bytecode this reduces to about 3.5KiB. This was tested with the\n`asyntest.py` demo, comparing the free RAM with that available running a\nsimilar script which exchanges uncompressed data. The difference was taken to\nbe the library overhead of running compression and asynchronous decompression.\n\nThis version is a subset of the original. Support was removed for features\nthought unnecessary for microcontroller use. The principal example is that of\ntimestamps. MicroPython does not support the `datetime` module. There are also\nissues with platforms differing in their time handling, notably the epoch. On a\nmicrocontroller it is simple to send the integer result from `time.time()` or\neven `time.time_ns()`. Python 2 support is removed. The code will run under\nPython 3.\n\nSupported types are fully compliant with a subset of the latest\n[MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md).\nIn particular, it supports the new binary, UTF-8 string and application-defined\next types. As stated above, timestamps are unsupported.\n\nThe repository includes `umsgpack/umsgpack_ext.py` which optionally extends the\nlibrary to support Python `set`, `complex` and `tuple` objects. The aim is to\nshow how this can easily be extended to include further types.\n\nThis MicroPython version uses various techniques to minimise RAM use including\n\"lazy imports\": a module is only imported on first usage. For example an\napplication that only de-serialises data using synchronous code will not import\ncode to dump data or that to support asynchronous programming.\n\n# 3. Installation\n\n## 3.1 Quick install\n\nThe library may be installed using the official `mpremote`:\n```bash\n$ mpremote mip install \"github:peterhinch/micropython-msgpack\"\n```\n\n## 3.2 Files\n\nThe following files are installed by `mpremote`:  \n1. `umsgpack/__init__.py` Necessary library support.  \n2. `umsgpack/mp_dump.py` Supports `dump` and `dumps` commands.  \n3. `umsgpack/mp_load.py` Supports `load` and `loads` commands.  \n4. `umsgpack/as_load.py` Support for `aload` command (asynchronous load).  \n5. `umsgpack/as_loader.py` Supports `aloader` asynchronous loader class.  \n6. `umsgpack/umsgpack_ext.py` Extends MessagePack to support `complex`, `set` and `tuple`.  \n7. `asyntest.py` Demo of asynchronous use of MessagePack.  \n8. `user_class.py` Demo of a user defined class that is serialisable by messagePack.  \n\nIn a minimal installation only items 1-3 are required.\n\nAdditional files:\n1. `asyntest_py3_serial` Demo of running the library on a PC.\n2. `run_test_suite` Bash script to run the full test suite.\n3. `test_umsgpack.py` The actual full test suite.\n\nSee [section 10](./README.md#10-test-suite) for details of the test suite.\n\n## 3.3 Manual install\n\nClone the repo by moving to a directory on your PC and issuing\n```bash\n$ git clone https://github.com/peterhinch/micropython-msgpack\n```\nCopy the directory `umsgpack` and its contents to your target hardware.\n\nThe following optional files may also be copied to the target:\n * `user_class.py` A serialisable user class.\n * `asyntest.py` Demo of asynchronous use. See [section 7](./README.md#7-asynchronous-use).\n\nThe files `run_test_suite` and `test_umsgpack.py` comprise the test suite which\nruns on a PC. See [section 9](./README.md#9-test-suite).\n\nIf RAM usage is to be minimised, the file `umsgpack/umsgpack_ext.py` may be\ndeleted from the target with loss of its additional type support. Its overhead\nis about 2KiB measured on a Pyboard with no frozen bytecode.\n\n# 4. API\n\nIn applications using `ujson`, MessagePack provides a drop-in replacement with\nthe same API. The human readable (but large) strings are replaced by compact\nbinary `bytes` objects.\n\nThe API supports the following methods:\n 1. `dumps(obj, **options)` Pack an arbitrary Python object. Returns a `bytes`\n instance.\n 2. `dump(obj, fp, **options)` Pack a Python object to a stream or file.\n 3. `loads(s, **options)` Deserialise a `bytes` object. Returns a Python\n object.\n 4. `load(fp, **options)` Unpack data from a stream or file.\n 5. `aload(fp, **options)` Asynchronous unpack of data from a `StreamReader`.\n See [section 7](./README.md#7-asynchronous-use).\n\nThe options are discussed below. Most are rather specialised. I am unsure if\nthere is a practical use case for `ext_handlers`: an easier way is to use\n[the ext_serializable decorator](./README.md#61-the-ext_serializable-decorator).\n\n## 4.1 Load options\n\n`load`, `loads` and `aload` support the following options as keyword args:  \n 1. `allow_invalid_utf8` (bool): unpack invalid strings into bytes (default\n `False` which causes an exception to be raised).\n 2. `use_ordered_dict` (bool): unpack dicts into `OrderedDict`, instead of\n `dict`. (default `False`).\n 3. `use_tuple` (bool): unpacks arrays into tuples, instead of lists (default\n `False`). The extension module (if used) makes this redundant.\n 4. `ext_handlers`: a dictionary of Ext handlers, mapping integer Ext type to a\n callable that unpacks an instance of Ext into an object. See\n [section 8](./README.md#8-ext-handlers).\n 5. `observer` (aload only): an object with an update() method, which is\n called with the results of each readexactly(n) call. This could be used, for\n example, to calculate a CRC value on the received message data.\n\nWork is in progress to make `dict` instances ordered by default, so option 3\nmay become pointless. The `umsgpack_ext` module enables tuples to be encoded in\na different format to lists which is more flexible than the global `use_tuple`\narg.\n\n## 4.2 Dump options\n\n`dump` and `dumps` support the following options as keyword args:  \n\n 1. `force_float_precision` (str): `\"single\"` to force packing floats as\n IEEE-754 single-precision floats, `\"double\"` to force packing floats as\n IEEE-754 double-precision floats. By default the precision of the target's\n firmware is detected and used.\n 2. `ext_handlers` (dict): dictionary of Ext handlers, mapping a custom type\n to a callable that packs an instance of the type into an Ext object. See\n [section 8](./README.md#8-ext-handlers).\n\n# 5. Extension module\n\nThe `umsgpack_ext` module extends `umsgpack` to support `complex`, `set` and\n`tuple` types, but its design facilitates adding further Python built-in types\nor types supported by other libraries. Support is entirely transparent to the\napplication: the added types behave in the same way as native types.\n\nThe following examples may be pasted at the REPL:\n```python\nimport umsgpack\nwith open('data', 'wb') as f:\n   umsgpack.dump(1 + 4j, f)  # mpext() handles extension type\n```\nReading back:\n```python\nimport umsgpack\nwith open('data', 'rb') as f:\n    z = umsgpack.load(f)\nprint(z)  # z is complex\n```\n The file `umsgpack_ext.py` may be found in the `umsgpack` directory. To extend\n it to support additional types, see\n[section 11](./README.md#11-notes-on-the-extension-module).\n\n# 6. Serialisable user classes\n\nAn example of a serialisable user class may be found in `user_class.py`. It\nprovides a `Point3d` class representing a point in 3D space stored as three\n`float` values. It may be run as follows (paste at the REPL):\n```python\nimport umsgpack\nfrom user_class import Point3d\np = Point3d(1.0, 2.1, 3)\ns = umsgpack.dumps(p)\nprint(umsgpack.loads(s))\n```\n\n## 6.1 The ext_serializable decorator\n\nThis provides a simple way of extending MessagePack to include additional\ntypes. The following is the contents of `user_class.py`:\n```python\nimport umsgpack\nimport struct\n\n@umsgpack.ext_serializable(0x10)\nclass Point3d:\n    def __init__(self, x, y, z):\n        self.v = (float(x), float(y), float(z))\n\n    def __str__(self):\n        return \"Point3d({} {} {})\".format(*self.v)\n\n    def packb(self):\n        return struct.pack(\"\u003efff\", *self.v)\n\n    @staticmethod\n    def unpackb(data):\n        return Point3d(*struct.unpack(\"\u003efff\", data))\n```\nA class defined with the decorator must provide the following methods:\n * Constructor: stores the object to be serialised.\n * `packb` This returns a `bytes` instance containing the serialised object.\n * `unpackb` Defined as a static method, this accepts a `bytes` instance of\n packed data and returns a new instance of the unpacked data type.\n\nTypically this packing and unpacking is done using the `struct` module, but in\nthe some simple cases it may be done by umsgpack itself. The following, taken\nfrom the extension module, illustrates support for `complex`:\n```python\n@umsgpack.ext_serializable(0x50)\nclass Complex:\n    def __init__(self, c):\n        self.c = c\n\n    def __str__(self):\n        return \"Complex({})\".format(self.c)\n\n    def packb(self):\n        return struct.pack(\"\u003eff\", self.c.real, self.c.imag)\n\n    @staticmethod\n    def unpackb(data):\n        return complex(*struct.unpack(\"\u003eff\", data))\n```\n\n# 7. Asynchronous use\n\n## 7.1 Serialisation\n\nSerialisation presents no problem in asynchronous code. The following example\nserialises the data using the normal synchronous `dumps` method then sends it\nasynchronously:\n```python\nasync def sender():\n    swriter = asyncio.StreamWriter(uart, {})\n    obj = [1, 2, 3.14159]\n    while True:\n        s = umsgpack.dumps(obj)  # Synchronous serialisation\n        swriter.write(s)\n        await swriter.drain()  # Asynchonous transmission\n        await asyncio.sleep(5)\n        obj[0] += 1\n```\n\n## 7.2 De-serialisation\n\nThis is potentially difficult. In the case of ASCII protocols like JSON and\nPickle it is possible to append a `b'\\n'` delimiter to each message, then use\n`StreamReader.readline()` to perform an asynchronous read of an entire message.\nThis works because the messages themselves cannot contain that character.\nMessagePack is a binary protocol so the data may include all possible byte\nvalues. Consequently a unique delimiter is unavailable.\n\nMessagePack messages are binary sequences whose length is unknown to the\nreceiver. Further, in many case a substantial amount of the message must be\nread before the total length can be deduced. The solution adopted is to add an\n`aload()` method that accepts data from a `StreamReader`, decoding it as it\narrives. The following is an example of an asynchronous reader:\n```python\nasync def receiver():\n    sreader = asyncio.StreamReader(uart)\n    while True:\n        res = await umsgpack.aload(sreader)\n        print('Recieved', res)\n```\n\nAlternatively, instead of using the `aload()` method, an `aloader` class can be\ninstantiated and utilized. For example:\n```python\nasync def receiver():\n    uart_aloader = umsgpack.aloader(asyncio.StreamReader(uart))\n    while True:\n        res = await uart_aloader.load()\n        print('Received', res)\n```\n\nThe demo `asyntest.py` runs on a Pyboard with pins X1 and X2 linked. See code\ncomments for connections with other platforms. The code includes notes regarding\nRAM overhead.\n\nThe demo `asyntest_py3_serial.py` is similar, but meant to run on a computer with full python3.\n\n# 8. Ext Handlers\n\nThis is an alternative to the `ext_serializable` decorator and provides another\noption for extending MessagePack. In my view it is rather clunky and I struggle\nto envisage a use case. It is included for completeness.\n\nThe packing functions accept an optional `ext_handlers` dictionary that maps\ncustom types to callables that pack the type into an Ext object. The callable\nshould accept the custom type object as an argument and return a packed\n`umsgpack.Ext` object.\n\nExample for packing `set` and `complex` types into Ext objects with type codes\n0x20 and 0x30:\n\n```python\numsgpack.dumps([1, True, {\"foo\", 2}, complex(3, 4)],\n    ext_handlers = {\n       set: lambda obj: umsgpack.Ext(0x20, umsgpack.dumps(list(obj))),\n       complex: lambda obj: umsgpack.Ext(0x30, struct.pack(\"ff\", obj.real, obj.imag))\n       })\n```\n\nSimilarly, the unpacking functions accept an optional `ext_handlers` dictionary\nthat maps Ext type codes to callables that unpack the Ext into a custom object.\nThe callable should accept a `umsgpack.Ext` object as an argument and return an\nunpacked custom type object.\n\nExample for unpacking Ext objects with type codes 0x20, and 0x30 into `set` and\n`complex` objects:\n\n``` python\numsgpack.loads(s,\n    ext_handlers = {\n      0x20: lambda ext: set(umsgpack.loads(ext.data)),\n      0x30: lambda ext: complex(*struct.unpack(\"ff\", ext.data)),\n    })\n```\n\nExample for packing and unpacking a custom class:\n\n``` python\nclass Point(object):\n    def __init__(self, x, y, z):\n        self.x = x\n        self.y = y\n        self.z = z\n\n    def __str__(self):\n        return \"Point({}, {}, {})\".format(self.x, self.y, self.z)\n\n    def pack(self):\n        return struct.pack(\"\u003eiii\", self.x, self.y, self.z)\n\n    @staticmethod\n    def unpack(data):\n        return Point(*struct.unpack(\"\u003eiii\", data))\n\n# Pack\nobj = Point(1,2,3)\ndata = umsgpack.dumps(obj, ext_handlers = {Point: lambda obj: umsgpack.Ext(0x10, obj.pack())})\n\n# Unpack\nobj = umsgpack.loads(data, ext_handlers = {0x10: lambda ext: Point.unpack(ext.data)})\n```\n# 9. Exceptions\n\nThese are defined in `umsgpack/__init__.py`.\n\nThe `dump` and `dumps` methods can throw the following:\n```python\n# Packing error\nclass UnsupportedTypeException(PackException):\n    \"Object type not supported for packing.\"\n```\nThe `load` and `loads` methods can throw the following. In practice these are\nonly likely to occur if data has been corrupted, for example if transmitted via\nan unreliable medium:\n```python\nclass InsufficientDataException(UnpackException):\n    \"Insufficient data to unpack the serialized object.\"\n\nclass InvalidStringException(UnpackException):\n    \"Invalid UTF-8 string encountered during unpacking.\"\n\nclass ReservedCodeException(UnpackException):\n    \"Reserved code encountered during unpacking.\"\n\nclass UnhashableKeyException(UnpackException):\n    \"\"\"\n    Unhashable key encountered during map unpacking.\n    The serialized map cannot be deserialized into a Python dictionary.\n    \"\"\"\n\nclass DuplicateKeyException(UnpackException):\n    \"Duplicate key encountered during map unpacking.\"\n```\n\n# 10. Test suite\n\nThis is mainly of interest to those wanting to modify the code.\n\nThe repo includes the test suite `test_umsgpack.py` which must be run under\nPython 3 in a directory containing the `umsgpack` tree. It will not run under\nMicroPython because it tests large data types: the test suite causes memory\nerrors when compiled under even the Unix build of MicroPython. The file\n`umsgpack_ext.py` should not be present: this is because the test suite assumes\nthat `complex` and `set` are not supported. The script `run_test_suite` renames\n`umsgpack_ext.py`, runs the tests and restores the file.\n\n# 11. Changes for MicroPython\n\nCode in this repo is based on\n[this implementation](https://github.com/vsergeev/u-msgpack-python), whose code\nis of high quality. It required minimal changes to run under MicroPython.\n\nHosted on a microcontroller its RAM consumption was high; most changes were to\nreduce this. Further, the nature of the protocol results in an issue when using\nasynchronous code to de-serialise data which arrives slowly or sporadically.\nThis is handled by adding an asynchronous de-serialiser.\n\nThese can be summarised as follows:  \nPython2 code removed.  \nCompatibility mode removed.  \nTimestamps removed.  \nConverted to Python package with lazy import to save RAM.  \nProvide asyncio StreamReader support.\nExported functions now match ujson: dump, dumps, load, loads (only).  \nMany functions refactored to save bytes, e.g. replacing the function dispatch\ntable with code.  \nFurther refactoring to reduce allocations.  \n`InvalidString` class removed because it is a subclass of a native type.  \nMethod of detecting platform's float size changed (MicroPython does not support\nthe original method).  \nVersion reset to (0.1.0).\n\n# 12. Notes on the extension module\n\nThese notes are for those wishing to understand how this works, perhaps to add\nsupport for further types.\n\nThe `mp_dump.py` attempts to load a function `mpext` from the module. If this\nfails (becuase the module is missing) it creates a dummy function. When the\n`dump` method runs, it executes `mpext` passing the object to be encoded. If\nthe type of the object matches one support by the extension, it returns an\ninstance of a serialisable class created with the passed object. If the type\ndoes not match, the passed object is returned for `dump` to inspect.\n\nSupporting additional types therefore comprises the following:\n 1. Create an `ext_serializable` class for the new type as per\n [section 6.1](./README.md#61-the-ext_serializable-decorator).\n 2. Change the function `mpext` to check for the new type and, if found, return\n an instance of the above class.\n\n## Acknowledgements\n\nThis project was inspired by\n[this forum thread](https://forum.micropython.org/viewtopic.php?f=15\u0026t=10827)\nwhere user WZab performed an initial port of the source library. See also\n[this GitHub issue](https://github.com/micropython/micropython/issues/4241).\n\n## Summary of references\n\nMessagePack main site:  \n[MessagePack](http://msgpack.org/)  \nMessagePack spec:  \n[the MessagePack spec](https://github.com/msgpack/msgpack/blob/master/spec.md).  \nCode on which this repo is based:  \n[u-msgpack-python](https://github.com/vsergeev/u-msgpack-python).\n\n## License\n\nmicropython-msgpack is MIT licensed. See the included `LICENSE` file for more\ndetails.\n","funding_links":[],"categories":["Libraries"],"sub_categories":["Communications"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterhinch%2Fmicropython-msgpack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterhinch%2Fmicropython-msgpack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterhinch%2Fmicropython-msgpack/lists"}