{"id":16140847,"url":"https://github.com/ellerbach/legodimensions","last_synced_at":"2025-06-23T01:07:13.808Z","repository":{"id":151527961,"uuid":"620329363","full_name":"Ellerbach/LegoDimensions","owner":"Ellerbach","description":"Read and write tags from Lego Dimensions. Interact with the portal.","archived":false,"fork":false,"pushed_at":"2025-05-06T09:06:42.000Z","size":147,"stargazers_count":24,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-06-20T01:37:56.839Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","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/Ellerbach.png","metadata":{"files":{"readme":"README.md","changelog":"changelog-config.json","contributing":null,"funding":null,"license":"LICENSE.md","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":"2023-03-28T13:22:46.000Z","updated_at":"2025-05-22T14:34:06.000Z","dependencies_parsed_at":"2023-06-03T03:45:55.743Z","dependency_job_id":"fdb2bf39-c6a8-4f51-8f53-f89b5c1d6411","html_url":"https://github.com/Ellerbach/LegoDimensions","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/Ellerbach/LegoDimensions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ellerbach%2FLegoDimensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ellerbach%2FLegoDimensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ellerbach%2FLegoDimensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ellerbach%2FLegoDimensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ellerbach","download_url":"https://codeload.github.com/Ellerbach/LegoDimensions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ellerbach%2FLegoDimensions/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261392176,"owners_count":23151718,"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-10-09T23:53:56.517Z","updated_at":"2025-06-23T01:07:08.770Z","avatar_url":"https://github.com/Ellerbach.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lego Dimensions portal and tag .NET implementation\n\nThe aim of this work is to be able to use Lego Dimensions portal and tags outside of their original purpose. Idea is to automate the usage or the portal led, but as well be able to read tags, with or without the portal to automate other external elements from a simple Raspberry PI for example.\n\nYou will find a detailed documentation of the Lego Dimensions protocol [here](https://github.com/Ellerbach/LegoDimensions/blob/main/LegoDimensionsProtocol.md). I built this documentation as I did not find anything that detailed.\n\nFrench and want to join an amazing community of Adult fan of Lego (AFOL)? Check out [FreeLUG](https://www.freelug.org/), the best association for AFOL in France!\n\n## Lego Dimensions portal\n\nThe Lego Dimensions portals compatible with this implementation are all except the XBox ones.\n\nIn this document and in the code, the portal refers to the Lego Dimensions where you place the vehicles and characters.\n\nThe Lego Tag or just tag refers to the the vehicle or character that you building when playing the game. They are under the hook NFC cards.\n\n### Driver installation\n\nIn order to use the portal, you need to setup USB drivers.\n\nFor Windows users (7 and more)\n\n* Plug your Lego Dimensions portal.\n* Download [Zadig](https://zadig.akeo.ie/) and run it\n* Select \"LEGO Reader V2.10\" in the devices list (if necessary, use the Options\u003eList All Devices menu)\n* Select `WinUSB` (libusb will also work) in the list at right of the green arrow and press the install button.\n\nFor Linux users\n\n* Install libusb and libusb1 if not already installed like `sudo apt-get libusb-dev` on a Rapsberry Pi\n* Copy the [99-lego-dimensions.rules](https://github.com/Ellerbach/LegoDimensions/blob/main/linux-driver-rules/99-lego-dimensions.rules) file into `/etc/udev/rules.d/`. This will allow you to run the code from any user.\n* Reboot then device or apply the changes\n\n### Portal instantiation and basic usage\n\nThe code supports as many portal you you want. Each portal will instantiate a unique class. 2 static helpers are available to get an array of portal:\n\n```csharp\nusing LegoDimensions;\n\nvar portal = LegoPortal.GetFirstPortal();\nportal.LegoTagEvent += PortalLegoTagEvent;\n\nConsole.WriteLine(\"Press any key to exit\");\nwhile (!Console.KeyAvailable)\n{\n    Thread.Sleep(1000);\n}\n\nportal.Dispose();\nConsole.WriteLine(\"End of test\");\n```\n\nIn the previous example, you can register to Tag events. Those are related to adding and removing Lego tag from the portal.\n\nAs an example, you can add this callback function:\n\n```csharp\nvoid PortalLegoTagEvent(object? sender, LegoTagEventArgs e)\n{\n    Console.WriteLine($\"Tag is present: {e.Present} - UID: {BitConverter.ToString(e.CardUid)}\");\n    if (e.Present)\n    {\n        Console.WriteLine($\"Tag is a {e.LegoTag?.GetType().Name} - Name= {e.LegoTag?.Name}\");\n    }\n}\n```\n\nWhile placing and removing tags on the different pads, you'll get output like this:\n\n```text\nTag is present: True - UID: 04-CA-8A-B2-6D-40-80\nTag is a Vehicle - Name= Scooby Snack\nTag is present: True - UID: 04-64-74-FA-00-49-81\nTag is a Character - Name= Ethan Hunt\nTag is present: False - UID: 04-64-74-FA-00-49-81\n```\n\nThe event object contains more information like the index. The index and the pad are need once you want to do some specific operations.\n\n## Lego Dimensions pad color adjustment\n\nYou can adjust the leds present in the portal. Most functions will allow you to adjust one or all pads at the same time.\n\n### Set color immediately on a pad\n\nThe `SetColor` function allows you to set the color on one or all pads at the same time. You will find example bellow.\n\n```csharp\nConsole.WriteLine(\"Pad left blue\");\nportal.SetColor(Pad.Left, Color.Blue);\nThread.Sleep(1000);\nConsole.WriteLine(\"Pad center white\");\nportal.SetColor(Pad.Center, Color.White);\nThread.Sleep(1000);\nConsole.WriteLine(\"Pad right purple\");\nportal.SetColor(Pad.Right, Color.Red);\nThread.Sleep(1000);\nConsole.WriteLine(\"All yellow\");\nportal.SetColor(Pad.AllPads, Color.Yellow);\n```\n\n### Get color displayed on a specific pad\n\nYou can as well check the color that is currently displayed on a specific pad.\n\n```csharp\nConsole.WriteLine(\"All yellow\");\nportal.SetColor(Pad.AllPads, Color.Yellow);\nThread.Sleep(200);\nvar col = portal.GetColor(Pad.Left);\nConsole.WriteLine($\"Pad left color: {col.R}-{col.G}-{col.B}\");\n```\n\nIf for some reason, this function does not get a proper answer, the Black color is the one which will be returned.\n\n### Set colors immediately on all pads\n\nThis `SetColorAll` function will set a specific color on all pads. Each pad can had a different color. The difference with te `SetColor` function is that on the `SetColor` one, if you are using all pads, they will have the same color.\n\n```csharp\nConsole.WriteLine(\"Pad left Blue, center white, right red\");\nportal.SetColorAll(Color.White, Color.Blue, Color.Red);\n```\n\n### Flashing a pad\n\nYou can flash a pad or all at the same time with the same settings using the `Flash` function.\n\nThe FlashPad uses 3 settings:\n\n* The tick on setting will count how much time it will stay on. The smallest, the fastest.\n* The tick off setting will count how much time it will stay off. The smallest, the fastest.\n* The tick count represent the number of change operation. So a full blink is actually 2 counts. So odd number will always finish on the previous color while the even ones on the new color.\n\n```csharp\nConsole.WriteLine(\"Pad right azure flashing 20 times (10 on and 10 off)\");\nportal.Flash(Pad.Right, new FlashPad(20, 20, 20, Color.Azure));\n```\n\n### Flashing all pads\n\nThis `FlashAll` is similar to the `Flash` one except you can individually set a specific configuration for each pad. Settings are the same.\n\nYou have one additional setting: `Enabled` which allow to have the pad switched off or on.\n\n```csharp\nConsole.WriteLine(\"Pad center red asymmetric flashing 40 times (20 on and 20 off)\");\nConsole.WriteLine(\"Pad left green flashing forever times fast (10 on and 10 off)\");\nConsole.WriteLine(\"Pad right azure flashing 20 times (10 on and 10 off)\");\nportal.FlashAll(new FlashPad(10, 30, 40, Color.Red), new FlashPad(1, 1, 255, Color.Green), new FlashPad(20, 20, 20, Color.Azure));\n```\n\n### Fading a pad\n\nYou can fade one or all pads at the same time. The fading speed is determined by the first parameter in the `FadePad` class and the count by the second. A count is a pulse, so either fading down either up, so you'll need 2 to have a full down then up. For the speed, the high the number is, the slowest is the pulse.\n\n```csharp\nConsole.WriteLine(\"Fade center pad relatively slowly from the displayed color to red, will finish on the new color as odd number of count.\");\nportal.Fade(Pad.Center, new FadePad(50, 5, Color.Red));\n```\n\n### Fading all pads\n\nAs for the `Fade`, you can use `FadeAll` to fade all pads at the same time. Each pad can have its own setting. Settings are the same. You have an additional one `Enabled` which allow to have the pad activated or not.\n\n```csharp\nConsole.WriteLine(\"Fade center pad relatively slowly from the displayed color to red, will finish on the new color as odd number of count.\");\nConsole.WriteLine(\"Fade left pad relatively quit fast from the displayed color to green, will finish on the old color as even number of count.\");\nConsole.WriteLine(\"Fade right will not fade as disabled regardless of the values placed in the Fade Pad.\");\nportal.FadeAll(new FadePad(50, 5, Color.Red), new FadePad(5, 50, Color.Green), new FadePad(10, 100, Color.Yellow, false));\n```\n\n### Fading a pad to a random color\n\nThis can be used to fade a pad on a random color, the speed and the count are the same settings as for the other fade functions.\n\n```csharp\nConsole.WriteLine(\"Randome fading on left pad\");\nportal.FadeRandom(Pad.Left, 10, 10);\nThread.Sleep(1000);\nConsole.WriteLine(\"Randome fading on center pad\");\nportal.FadeRandom(Pad.Center, 1, 100);\nThread.Sleep(1000);\nConsole.WriteLine(\"Randome fading on right pad\");\nportal.FadeRandom(Pad.Right, 5, 15);\nThread.Sleep(1000);\n```\n\n### Reading tags\n\nYou can read tags when present on the pad. The following example shows how you can dump a full tag:\n\n```csharp\nConsole.WriteLine(\"Place at least 1 tag on the portal, this will dump the full tag.\");\nwhile (!portal.PresentTags.Any())\n{\n    Thread.Sleep(1000);\n}\n\nvar idx = portal.PresentTags.First().Index;\nfor (byte i = 0; i \u003c 0x2c; i += 4)\n{\n    var tag = portal.ReadTag(idx, i);\n    if (tag.Length == 0)\n    {\n        Console.WriteLine($\"Error reading card page 0x{i:X2}\")\n    }\n    else\n    {\n        Console.WriteLine($\"Tag: {BitConverter.ToString(tag)}\");\n    }\n}\n```\n\nIf there will be an error reading the card, you'll get an empty buffer. This can happen if you are moving the card of if you're using a non initialized card. See the section on the Lego Tags to understand how to initialize a card.\n\n### Write a tag\n\nTo be documented later.\n\n### List tags on the pad\n\nYou should keep track of the tags with their UID. But a list of available tab with their index and associated tag is available through the `PresentTags` property as well as when calling the `ListTags` function.\n\n## Lego Tags\n\nThe [Lego Dimensions library](https://github.com/Ellerbach/LegoDimensions/tree/main/LegoDimensions) contains the full support to handle the Lego Tags. A full solution detecting the tags, reading them and displaying the characters and vehicles is available in the [Lego Dimensions Read NFC project](https://github.com/Ellerbach/LegoDimensions/tree/main/LegoDimensionsReadNfc).\n\nThe Read NFC project is currently using a [PN532](https://github.com/dotnet/iot/tree/main/src/devices/Pn532) connected in serial to read the tags. It's based on the [.NET IoT](https://github.com/dotnet/iot) implementation for both the reader and the Ultralight tags.\n\nThe example is complete and gives all the steps to:\n\n* Connect the PN532\n* Get a card, process the ultralight card\n* Generate the authentication key (4 bytes) to read the protected data tags\n* Read the pages 0x24, 0x25, 0x26 and 0x27 where the actual tag elements are stored\n* Process and decrypt the vehicle or character information\n* Display the vehicle or character information\n* Do this over and over, detect new cards, handle issues with non supported cards or potential errors\n\nYou just need to adjust the serial port used (auto detection can be an improvement). Mind the name of the port that can be different depending on which operating system you'll run it. This code will work on Windows, Linux and MacOS wherever .NET 6.0 and further is supported.\n\nUnit Tests are also provided to make sure any change will not break the encoding/decoding.\n\n### Reading card data on an external NFC reader like a PN532\n\nThe ultralight NFC card is protected with password. This password is a 4 bytes. It can be processed with the `LegoTag` class with the `PasswordGenerator` function:\n\n```csharp\nDebug.WriteLine(\"Generating authentication key\");\nultralight.AuthenticationKey = LegoTag.GenerateCardPassword(ultralight.SerialNumber);\nDebug.WriteLine($\"Authentication key: {BitConverter.ToString(ultralight.AuthenticationKey)}\");\nultralight.Command = UltralightCommand.PasswordAuthentication;\nvar auth = ultralight.RunUltralightCommand();\n\n// read page 0x24 to 0x27\nultralight.BlockNumber = 0x24;\nultralight.Command = UltralightCommand.Read16Bytes;\nvar res = ultralight.RunUltralightCommand();\n// Check we do have a result\nif (res \u003e 0)\n{\n    // Process the data\n}\n```\n\n### Processing data\n\nTo process the data, you'll need a valid read from the previous section. It's a 16 byte buffer.\n\n```csharp\n// If page 0x26 == 00 01 00 00 we have a vehicle\nif (LegoTag.IsVehicle(ultralight.Data.AsSpan(8, 4).ToArray()))\n{\n    Console.WriteLine(\"Found a vehicle.\");\n    // The 2 first one used\n    var id = LegoTag.GetVehicleId(ultralight.Data);\n    Console.Write($\"vehicle ID: {id} \");\n    Vehicle vec = Vehicle.Vehicles.FirstOrDefault(m =\u003e m.Id == id);\n    if (vec is not null)\n    {\n        Console.WriteLine($\"{vec.Name} - {vec.World}\");\n    }\n    else\n    {\n        Console.WriteLine(\"and vehicle does not exist!\");\n    }\n}\nelse\n{\n    Console.WriteLine(\"Found a character.\");\n    var id = LegoTag.GetCharacterId(ultralight.SerialNumber, ultralight.Data.AsSpan(0, 8).ToArray());\n    Console.Write($\"Character ID: {id} \");\n    Character car = Character.Characters.FirstOrDefault(m =\u003e m.Id == id);\n    if (car is not null)\n    {\n        Console.WriteLine($\"{car.Name} - {car.World}\");\n    }\n    else\n    {\n        Console.WriteLine(\"and character does not exist!\");\n    }\n}\n```\n\nAs a result, by placing different tags, you'll get something like:\n\n``` text\nPlace the tag on the reader!\nFound a vehicle.\nvehicle ID: 123 Ghost Trap - Ghostbusters\nFound a character.\nCharacter ID: 42 Wicked Witch - Wizard of Oz\nFound a character.\nCharacter ID: 2 Gandalf - Lord of the Rings\nFound a vehicle.\nvehicle ID: 11 The Annihilator - The Lego Movie\n```\n\n### Writing data on a new NTAG213\n\nThe regular Lego Tag are protected and except the vehicle tag that can be written at any time, you can't change the character ID of a normal Lego tag. But you can write all this on a brand new tag.\n\nIf your tag is fully new, then you can just write whatever you want. You have the code in the [LegoDimensionsReadNfc application](https://github.com/Ellerbach/LegoDimensions/tree/main/LegoDimensionsReadNfc). This application is available in the Releases both for Windows and arm 32 (so you can run it on a Raspberry Pi).\n\nThis application is a wizard, so just follow the steps! You can erase a card, write a card, read a card and fully dump a card. This application is a console application but has a nice UX and UI!\n\nThis application requires a PN532 to work.\n\n#### If you want to write your own code to writ tags\n\nIn short, the important steps are the following:\n\n1. Detect the tag\n1. Create a new password based on the Card Unique ID (the 7 bytes) using the `LegoTag.GenerateCardPassword`\n1. Write it to the 0x2B block on the card (assuming an NTAG213, address is different for 215 and 216)\n1. If you want a vehicle:\n\n    1. Use `LegoTag.EncryptVehicleId` to get the 4 bytes\n    1. Write the 4 bytes on block 0x24\n    1. Write `{ 0x00, 0x01, 0x00, 0x00 }` on block 0x26\n\n1. If you want a character:\n\n    1. Use `LegoTag.EncrypCharactertId` to get the 8 bytes\n    1. Write the first 4 one on block 0x24\n    1. Write the next 4 one on block 0x25\n    1. If you've been using a vehicle before, write `{ 0x00, 0x00, 0x00, 0x00 }` on block 0x26\n\nAnd now, you're all set! No need to adjust any other password!\n\n## Sources of inspiration and code\n\n* [LD-ToyPad-Emulator](https://github.com/Berny23/LD-ToyPad-Emulator)\n* [liblegodimensionsportal](https://github.com/GrahamBriggs/liblegodimensionsportal)\n* [lego_dimensions_protocol](https://github.com/woodenphone/lego_dimensions_protocol)\n* [ldnfctags](https://github.com/phogar/ldnfctags)\n* [jtoypad](https://github.com/e-amzallag/jtoypad)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fellerbach%2Flegodimensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fellerbach%2Flegodimensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fellerbach%2Flegodimensions/lists"}