[Bug] Generating qr.nc fail : Error: IndexError('bytes index out of range',)
Describe the bug
Printer driver is set to cnc.
It doesn't matter if you load the wallet from a new mnemonic or from a qr scan.
After wallet is loaded, I do : Backup Mnemonic > Encrypted > Encrypted QR Code > Type key and proceed without custom id
When presented with QR code on device screen, I do : Print as QR > yes to "cnc/file" > and I quickly get a screen showing in red "Error: IndexError('bytes index out of range',)"
The sd card is recognized, I could save qr as images, and I see an empty "qr.nc" file with updated timestamp but 0 bytes size.
I tried to modify the cnc driver setup, without success, and I retested with default cnc driver setup.
Issue happen on my wonder mv device. Issue happen on the simulator for wonder my and also for amigo.
I am now trying to build a firmware with MICROPY_ENABLE_COMPILER to see more infos. I have a cnc on grbl, that would be another step.
Device(s) affected
- [ ] Amigo
- [ ] M5stickV
- [ ] Cube
- [ ] Dock
- [ ] Bit
- [ ] Yahboom
- [X] WonderMV
Version affected
- [X] Official release (selfcustody/krux): v25.03.0
- [ ] Beta release (odudex/krux_binaries): vXX.YY.Z-betaWW
Export to CNC feature is "abandoned", as non of devs have one to test, and, until now, we didn't know any user making use of it. I was even suggesting to remove it. But since we have you now, and it seems you're motivated to make it work, we can try to help! I'll try to track and solve the error on my side too
I don't think you need to enable compiler, just comment these lines of the try/except and build the firmware using the script /.krux build maixpy_yahboom (or other device name) and flash the firmware onto device using instructions here. Then follow these instructions to use MaixPy IDE and see the exception on terminal.
I am on silicon Mac but I could compile and build a new firmware using the linux how to : krux build maixpy_wonder_mv . To flash I didn't find out how to pass the -p and -B params to krux flash so I used odudex ktool-mac, replacing the contents of maixpy_wonder_mv with my binaries : ktool-mac -p /dev/cu.usbserial-xxxxxx -B dan -b 1500000 maixpy_wonder_mv/kboot.kfpkg . Seems it went through, I see my updated version in the about screen on device.
In this section, I removed line "self.ctx.display.clear()", but it doesn't help having more details on the issue.
About MaixPy IDE, I tried, seems it connect as the device is rebooting, but it never complete and the device stay frozen. As I see no "wonder mv" in the supported devices list, could I do something ?
Change this to see the error in the terminal -> remove the try / except block.
From this:
try:
self.ctx.display.clear()
status = item[1]()
if status != MENU_CONTINUE:
return status
except Exception as e:
self.ctx.display.to_portrait()
self.ctx.display.clear()
self.ctx.display.draw_centered_text(
t("Error:") + "\n%s" % repr(e), theme.error_color
)
self.ctx.input.wait_for_button()
To this:
self.ctx.display.clear()
status = item[1]()
if status != MENU_CONTINUE:
return status
Then to connect to the device and see the output in terminal copy the below file (kboot.py.txt rename to remove the .txt at the end) to the project root dir and execute (maybe you will need to install dependencies, use poetry install --all-extras to install everything including dependencies needed to run our simulator):
poetry python ktool.py -t
if you don't see nothing changing in the terminal try to reset the device while connected or to use the cmd with this param:
poetry python ktool.py -t -B dan
If you want to test faster, use our simulator with sd card enabled (a folder sd will be created on the project's simulator folder):
poetry run poe simulator --sd
I've tested on the simulator and can confirm that this error appears:
The stacktrace below appears on terminal (with the change mentioned above) :
Exception in thread Thread-1 (run_krux):
Traceback (most recent call last):
File "/usr/lib/python3.12/threading.py", line 1073, in _bootstrap_inner
self.run()
File "/usr/lib/python3.12/threading.py", line 1010, in run
self._target(*self._args, **self._kwargs)
File "/krux/simulator/simulator.py", line 130, in run_krux
exec(boot_file.read())
File "<string>", line 159, in <module>
File "<string>", line 133, in home
File "/krux/src/krux/pages/__init__.py", line 512, in run
_, status = self.menu.run_loop(start_from_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 686, in run_loop
status = self._clicked_item(selected_item_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 718, in _clicked_item
status = item[1]()
^^^^^^^^^
File "/krux/src/krux/pages/home_pages/home.py", line 71, in backup_mnemonic
return mnemonics_viewer.mnemonic()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/home_pages/mnemonic_backup.py", line 46, in mnemonic
submenu.run_loop()
File "/krux/src/krux/pages/__init__.py", line 686, in run_loop
status = self._clicked_item(selected_item_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 718, in _clicked_item
status = item[1]()
^^^^^^^^^
File "/krux/src/krux/pages/home_pages/mnemonic_backup.py", line 87, in encrypt_mnemonic_menu
return encrypt_mnemonic_menu.encrypt_menu()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/encryption_ui.py", line 176, in encrypt_menu
_, _ = submenu.run_loop()
^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 686, in run_loop
status = self._clicked_item(selected_item_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 718, in _clicked_item
status = item[1]()
^^^^^^^^^
File "/krux/src/krux/pages/encryption_ui.py", line 280, in encrypted_qr_code
seed_qr_view.display_qr(allow_export=True)
File "/krux/src/krux/pages/qr_view.py", line 505, in display_qr
_, status = submenu.run_loop()
^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 686, in run_loop
status = self._clicked_item(selected_item_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/pages/__init__.py", line 718, in _clicked_item
status = item[1]()
^^^^^^^^^
File "/krux/src/krux/pages/qr_view.py", line 435, in print_qr
utils.print_standard_qr(self.code, title=title, is_qr=True)
File "/krux/src/krux/pages/utils.py", line 50, in print_standard_qr
print_page.print_qr(data, qr_format, title, width, is_qr)
File "/krux/src/krux/pages/print_page.py", line 62, in print_qr
self._send_qr_to_printer(data)
File "/krux/src/krux/pages/print_page.py", line 48, in _send_qr_to_printer
self.printer.print_qr_code(qr_code)
File "/krux/simulator/kruxsim/mocks/machine.py", line 40, in new_print_qr_code
return old_print_qr_code(qr_code)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/krux/src/krux/printers/cnc.py", line 255, in print_qr_code
super().print_qr_code(qr_code)
File "/krux/src/krux/printers/cnc.py", line 72, in print_qr_code
while qr_code[size] != "\n":
~~~~~~~^^^^^^
IndexError: bytearray index out of range
Conclusion
For me it appears that CNC driver was built to deal with TEXT QRCodes only, but the Encrypted one is in binary form.
Thanks for help, with your code modification suggestion and that command "poetry run python ktool.py -t -p /dev/cu.usbserial-xxxx -B dan" I could see output clearly.
For some reason in my initial testing I saw the cnc option only for encrypted, but now I see the option for any kind of qr.
The error is also with plain text qr, seed qr, compact qr.
The simulator, can it keep the printer driver settings (default to cnc, etc) and load a qr from an existing source ? Right now to use it I have to set the driver every time and fastest I found is to create a mnemonic with 12 words.
I placed some debug infos in that block and checked for end of qr_code array loop by size instead of '\n' char, to go further without exception. That way I get a qr.nc generated but only with modal setting gcodes, nothing else.
Did the qr_code array structure changed since the last cnc tested version, around mid June 2023 as of this issue ? No matter the type of qr (plain or compact tested), the array is filled with numbers. I am no python expert but the code look for a '\n' whithout (I think) converting to char, and to decide whether to cut or not, it look for 0 or 1, which would I guess mean it's a hole in the qr, but there is very few 0 or 1 on the qr_code array.
Can you explain me how the qr_code array work ?
Sorry I didn't look at this issue yet, but I see you are digging deep.
Yes, the QR structure was optimized, from strings of zeros, ones and \n, to binary format, where each QR "pixel", or block, is represented by 1 bit. This is likely the cause of the issue.
Yes, if you run the simulator with --sd. This will create an sd folder inside simulator and enable access to files there. In settings you can change the Persist -> Location to SD card and this will save any changes you made to defaults.
To test this faster at "Home" screen, I like to change boot.py and "jump" Login by commenting its line here, then create a new Wallet in ctx (context object):
# login(ctx)
# gc.collect()
from krux.wallet import Wallet
from krux.key import Key
ctx.wallet = Wallet(Key("action action action action action action action action action action action action", False))
home(ctx)
As you have already identified, you can use an older version in order to test CNC for now. I think we can convert the QR code back to the non "optimized structure" before sending it to the CNC printer. This would solve the issue.
SD card and new wallet in simulator, great tip, thanks 👍
Does it make sense if I inspire from the thermal printer code to understand the optimized qr_code array and adapt the cnc code ? I don't have a thermal printer, I don't know if that part is up to date.
Does it make sense if I inspire from the thermal printer code to understand the optimized qr_code array and adapt the cnc code ? I don't have a thermal printer, I don't know if that part is up to date.
It does, shouldn't be complicated. Thermal printer is working. Do you have grbl gcode simulator/visualizer to inspect the results?
I have an openbuilds cnc that I want to test with, and I can inspect first with open builds control software.
Ok, did some progress here and here
It should reproduce the "action action ..." wallet in compact format
It miss / mix / mirror some data. For now if the bit is more than 0 I consider it as a dot. Will see.
The code so far is
def print_qr_code(self, qr_code):
"""Prints a QR code, scaling it up as large as possible"""
from ..qr import get_size
size = 0
size = get_size(qr_code)
print("size:",size)
print("self.part_size",self.part_size," / self.border_padding:",self.border_padding," / size:",size)
cell_size = (self.part_size - (self.border_padding * 2)) / size
print("cell_size:",cell_size)
# Modal settings
self.on_gcode("G17") # x/y plane
self.on_gcode("G20" if self.unit == "in" else "G21") # units
self.on_gcode("G40") # cancel diameter compensation
self.on_gcode("G49") # cancel length offset
self.on_gcode("G54") # coord system 1
self.on_gcode("G90") # non-incremental motion
self.on_gcode("G94") # feed/minute mode
print("self.cut_depth:",self.cut_depth," / self.pass_depth:",self.pass_depth)
scale = Settings().hardware.printer.thermal.adafruit.paper_width // size
scale *= Settings().hardware.printer.thermal.adafruit.scale # Scale in %
scale //= 200 # 100% * 2 because printer will scale 2X later to save data
# Being at full size sometimes makes prints more faded (can't apply too much heat?)
print("scale:",scale)
line_bytes_size = (size * scale + 7) // 8 # amount of bytes per line
print("line_bytes_size:",line_bytes_size)
#num_passes = math.ceil(self.cut_depth / self.pass_depth)
num_passes = 1
for p in range(num_passes):
print("pass #",p)
for row in range(size):
print("row #",row)
byte = 0
line_bytes = bytearray()
for col in range(size):
print("col #",col)
bit_index = row * size + col
print("bit_index #",bit_index)
bit = qr_code[bit_index >> 3] & (1 << (bit_index % 8))
cut = 0
if bit > 0:
cut = 1
print("bit:",bit," / cut:",cut)
if cut:
# Flip the y coord
y = row
x = col
x_index = x
if y % 2 == 0:
x_index = size - 1 - x
plunge_depth = min((p + 1) * self.pass_depth, self.cut_depth)
self.cut_cell(x_index, size - 1 - y, cell_size, plunge_depth)
Wow, didn't know that this exists https://software.openbuilds.com/ very nice!
Ok, did some progress here and here
You're close!
Indeed . Now the dots are correctly read.
def print_qr_code(self, qr_code):
"""Prints a QR code, scaling it up as large as possible"""
from ..qr import get_size
size = 0
size = get_size(qr_code)
print("size:",size)
print("self.part_size",self.part_size," / self.border_padding:",self.border_padding," / size:",size)
cell_size = (self.part_size - (self.border_padding * 2)) / size
print("cell_size:",cell_size)
# Modal settings
self.on_gcode("G17") # x/y plane
self.on_gcode("G20" if self.unit == "in" else "G21") # units
self.on_gcode("G40") # cancel diameter compensation
self.on_gcode("G49") # cancel length offset
self.on_gcode("G54") # coord system 1
self.on_gcode("G90") # non-incremental motion
self.on_gcode("G94") # feed/minute mode
print("self.cut_depth:",self.cut_depth," / self.pass_depth:",self.pass_depth)
#num_passes = math.ceil(self.cut_depth / self.pass_depth)
num_passes = 1
for p in range(num_passes):
print("pass #",p)
for row in range(size):
print("row #",row)
byte = 0
line_bytes = bytearray()
for col in range(size):
print("col #",col)
bit_index = row * size + col
print("bit_index #",bit_index)
bit = qr_code[bit_index >> 3] & (1 << (bit_index % 8))
cut = 0
if bit > 0:
cut = 1
print("bit:",bit," / cut:",cut)
if cut:
# Flip the y coord
y = row
x = col
x_index = x
plunge_depth = min((p + 1) * self.pass_depth, self.cut_depth)
self.cut_cell(x_index, size - 1 - y, cell_size, plunge_depth)
I added an "SVG" option to the "Save QR Image to SD card", this output an .svg, it use lib https://github.com/orsinium-labs/svg.py . That was useful for me to play with 3d printing, and real quick to do now that I got the qr_code format understood. Would it be interesting to add it to the trunk ?
Here the menu and the output
Here the encrypted ( password abc ) qr as svg
And an easy slice with prusa slicer, which is i.e. recognized as additional software in tails and the output, which was scanned ok
Am I right that it is only possible to export encrypted qr images and not compact, plain, etc ?
I think the nc code generation Is good now. It will take me some time to test on the machine, I need to do some additional work on it first.
Am I right that it is only possible to export encrypted qr images and not compact, plain, etc ?
Yes, for now we don't allow saving unencrypted secrets, even in image formats.
I'm impressed on how fast you got familiarized with the code, fixed bugs and implemented new features! We'll want a Pull Requests!
Also, please consider joining our Telegram group. People will love to follow your hacking progress with 3D printer and CNC!
Nice addition! But I think this SVG lib will not work on device, need to test... did you find a micropython implementation alternative for SVG?
Nice addition! But I think this SVG lib will not work on device, need to test... did you find a micropython implementation alternative for SVG?
Ah! Indeed you are right, it fail to fin the svg lib ... the output svg is very simple, I will see to code it.
Am I right that it is only possible to export encrypted qr images and not compact, plain, etc ?
Yes, for now we don't allow saving unencrypted secrets, even in image formats.
I'm impressed on how fast you got familiarized with the code, fixed bugs and implemented new features! We'll want a Pull Requests!
Also, please consider joining our Telegram group. People will love to follow your hacking progress with 3D printer and CNC!
Thank you, ! will do the pull, it's a very pleasant project to develop for, easy to setup, helper tools, and you helped me a lot.
I adapted the svg output to micropython. No additional lib. I don't like to write xml without a support lib but didn't find one for micropython, except a parser which I don't need. So hardcoded write of '<svg xmlns...' etc but I guess the structure is simple enough for this to be acceptable.
I tested again cnc and svg to sd card on the device, not only on the simulator.
I would like to make the pull request, could you instruct me a bit : shall I fork and create some specific branch ? naming ?
Wow, nice to know that! To create a PR:
- Fork the project from here (selfcustody).
- Clone the project into your machine
- On the folder create a new branch using
git checkout -b qr_cnc - Make changes to the files you needed
- Commit
git commit -mand type your message about what is in the commit - Push the changes to your branch
git push(for the first time, git will say the branch doesn't exist on your fork yet, only on your machine, so follow the cmd instructions it gives to you to it will be something likegit push --set-upstream origin) - Your branch
qr_cncand changes will appear on Github on your fork (your projects). - Navigate to your fork (your project) on github and a button will appear at top suggesting to create a PR on selfcustody based on the branch you recently pushed (created on github), just click on this button and follow instructions selecting the selfcustody
developbranch (alternatively you can enter in selfcustody/krux click on Pull request at top and then new and select the selfcustodydevelopbranch and youqr_cncbranch)
You can use VSCode to help with git related commands by using its interface if you prefer
Then we will see your changes on the PR here and comment about it to fix something in order to integrate the code.
Could you help me with the rx tx pins on the wondermv ? I connected the hardware correctly but I don't know if the 27 / 28 pin numbers I see on the app are correct and I didn't find any specific specs on wondermv pins setup. I uncommented on cnc.py as documented, but it always say "not connected" when I choose to print direct to cnc.
This is something I never tested too, on thermal printer. Pins 27 and 28 are correct for WonderMV
Thanks a lot. Where did you find those specs ? I ordered a m5stickv and a maix cube, so I can test other devices.
On thermal printer we always recommend to use the TX pin and GND only because Krux doesn't need to receive nothing from the printer, and connecting RX sometimes could cause issues.
Where did you find those specs ?
After intensive investigation :) https://drive.google.com/drive/folders/1Gy9MKHwWgql2Bpu28xJtZg6IAgV2TTyR
I can happily say that I see now grbl response messages - error messages but at least it communicates