LORE is a command-line utility for interacting with the Edison V3 robot.
It allows you to write programs for the Edison robot in a subset of the LOGO programming langauge, rather than EdScratch or EdPy. Whether you use LOGO or EdPy, in enables offline compilation and a utility to upload programs to the robot from the command-line.
For more information about the Edison V3 robot, you can visit the official resource page: Edison V3.0 Robot.
The vendor-supported programming environment for the Edison V3 robot involves using web-based interfaces like https://www.edpyapp.com/ or https://www.edscratchapp.com/. These platforms typically rely on remote APIs to compile your code remotely.
LORE enhances the development experience by providing:
- Local Development Workflow: Write MicroPython code in a local editor and manage your projects directly on your machine.
- Command-Line Uploads: Upload code to the robot directly from your terminal, bypassing the need to interact with web interfaces for flashing.
- Facilitation of AI-Assisted Coding: The tight feedback loop, without manual copy-pasting, creates an environment for using AI-assisted coding tools like Gemini, Claude Code, Cortex Code, and Codex to write and iterate on code for the Edison robot.
- Offline Compilation: LORE compiles locally using
mpy-cross, producing byte-identical output to the official remote Edison API. No internet connection required. The local pipeline includes an EdPy validator (rejects unsupported features like floats, strings, and lists), constant inlining, and a qstr remapper that matches the Edison firmware's internal string table. Remote compilation via the Edison API is available with--remote-compile.
LORE allows you to program the Edison V3 robot using either MicroPython or a subset of the LOGO language. The following examples both drive the Edison robot in an equilateral triangle.
REPEAT 3 [
FORWARD 100
LEFT 120
]
import Ed
Ed.EdisonVersion = Ed.V3
Ed.Speed = Ed.SPEED_4
for _ in range(3):
Ed.Drive(Ed.FORWARD, Ed.DISTANCE_CM(10), Ed.SPEED_4) # Drive forward 10cm
Ed.Rotate(Ed.LEFT, Ed.DEGREES_360(120), Ed.SPEED_4) # Turn left 120 degreesTo use LORE, follow these steps:
-
Activate Virtual Environment: It is recommended to work within a Python virtual environment. To create one:
python3 -m venv .venv
Then activate it:
source .venv/bin/activate -
Install Dependencies: Install the necessary Python libraries:
pip install pyusb requests lark
-
Create Your Application: Place your MicroPython
main.pyor LOGOmain.logofiles within theapps/<your_app_name>/directory. For example,apps/line_following/main.pyorapps/logo_square/main.logo. -
Build Your Application: Before your first build, compile
mpy-cross:make
Note:
makewill download and build MicroPython 1.27.0 (requires a C compiler).Then build your application:
./lore build <your_app_name>
For example:
./lore build nursery_rhymes
If you prefer to use the official Edison API (requires an internet connection):
./lore build <your_app_name> --remote-compile
-
Flash Your Application: Transfer the compiled
.mpyapplication to your Edison V3 robot:./lore flash <your_app_name>
For example:
./lore flash nursery_rhymes
The local compiler relies on two reverse-engineered tables embedded in the lore executable:
EDISON_CONSTANTS— mapsEd.CONSTANT_NAMEto integer values (e.g.,Ed.FORWARD=1)EDISON_QSTR_IDS— maps string names to the Edison firmware's static qstr IDs (e.g.,"Drive"=11)
These were discovered by probing the remote Edison compiler API at https://api.edisonrobotics.net/ep/compile/ep_compile_usb_v3. If Edison releases new firmware with additional API functions or constants, you can discover their values using the same technique.
To find the integer value of an Ed.CONSTANT, compile a program that assigns it to a variable and inspect the bytecode. The remote compiler inlines constants as integer literals:
import requests
API = "https://api.edisonrobotics.net/ep/compile/ep_compile_usb_v3"
code = """import Ed
Ed.EdisonVersion = Ed.V3
Ed.DistanceUnits = Ed.CM
Ed.Tempo = Ed.TEMPO_MEDIUM
x = Ed.NEW_CONSTANT
"""
r = requests.post(API, data=code, headers={"Content-Type": "text/plain"})
j = r.json()
if j.get("compile") == False:
print("Compile error:", j.get("message"))
else:
# The constant value will appear as an integer literal in the bytecode.
# Compare the hex output against a version using a known integer to
# identify where the value is encoded.
print("hex:", j["hex"])For small values (0-127), the integer appears directly in the bytecode as a LOAD_CONST_SMALL_INT operand. For larger values, use two compilations — one with the constant and one with a known integer — and diff the hex output to locate the encoded value.
To find the Edison firmware's qstr ID for a new API name, compile a program that uses it and examine the qstr table in the .mpy output:
import requests
API = "https://api.edisonrobotics.net/ep/compile/ep_compile_usb_v3"
code = """import Ed
Ed.EdisonVersion = Ed.V3
Ed.DistanceUnits = Ed.CM
Ed.Tempo = Ed.TEMPO_MEDIUM
Ed.NewFunction()
"""
r = requests.post(API, data=code, headers={"Content-Type": "text/plain"})
j = r.json()
if j.get("compile") == False:
print("Compile error:", j.get("message"))
else:
mpy = bytes.fromhex(j["hex"])
# Skip 4-byte header, read n_qstr varint, then decode qstr entries.
# Static entries are encoded as (id << 1) | 1; the new function's
# Edison qstr ID will appear here instead of as a dynamic string.
print("Raw mpy hex:", j["hex"])The qstr table starts at byte offset 4 in the .mpy file. Each entry is either a static reference (varint with low bit set, ID = value >> 1) or a dynamic string (varint with low bit clear, length = value >> 1, followed by the string bytes and a NUL terminator). The remote compiler converts Edison API names to static IDs — any unfamiliar static ID in the output is the Edison firmware's qstr ID for the new name.
For a complete working example, see the probe_qstrs.py script used during development in the project history.

