t-oster / LibLaserCut

a platform independant library to control Lasercutters. This is the base library for VisiCut
http://visicut.org
Other
59 stars 55 forks source link

Support for stock K40 controller boards #81

Closed dlarue closed 5 years ago

dlarue commented 6 years ago

Lots of people are using the inexpensive K40 laser cutter and one of the initial requirements was to "upgrade" the controller board to RAMPS or Smoothie, etc to gain access to other sofware besides the Corel software. This also allowed VisiCut usage( via LabLaserCut ) but now there is open source software called K40 Whisperer( open source python code ) which allows direct control of the K40 laser cutter and while it is not VisiCut, it runs on anything Python runs on and has become popular.

Has there been any consideration to add direct K40 control in VisiCut/LIbLaserCut now that there's example code showing how it can be done?

http://www.scorchworks.com/K40whisperer/k40whisperer.html https://github.com/dimsumlabs/k40_whisperer

morriswinkler commented 5 years ago

@t-oster Have there been any updates regarding that question, did you talk back to the author of k40 whisperer?

t-oster commented 5 years ago

Hi, here ist the answer: https://github.com/dimsumlabs/k40_whisperer/issues/1

tl;dr: It is possible, but no one has time to develop it. However I guess it should not take more than 10-15 dev hours (just guessing). So if you get enough people to fund this (and probably a K40 for testing), I could implement this during my work time (120€/dev hour).

tatarize commented 5 years ago

I'm a good way into writing a stock java driver for the Nano-K40 boards. https://github.com/K40Nano/Jk40

I spent a good while looking at the Whisperer code, and playing with the device itself itself and writing documentation for the whole thing: https://github.com/K40Nano/K40Nano/wiki and wrote a python library by extracting the important stuff from the tangle of whisperer code and giving python folks an api ( https://pypi.org/project/k40nano/ ).

I needed to rewrite the core elements to make it async and I wanted to allow more permissive licenses, which meant rewriting everything from scratch, so I switched to Java because I'm better at it, which seems rather serendipitous, given the language here.

So what exactly is needed to fully interface the stuff? I looked through the various drivers and I don't see any that are using usb connections. At the core you need to connect through the USB to the read the status endpoint and writing LHYMICRO-GL code to the data endpoint. I spent the better part of month solving the speedcodes, and the code the laser uses. So that stuff is all pretty reasonable. And within my wheelhouse. Would putting that together be basically enough to do everything?

mgmax commented 5 years ago

To be honest, which drugs did the manufacturer take when designing that data format? ;-)

We don't have any USB interface code in LibLaserCut, so you will need to bring your own.

What you need first is

Then, adapt sendJob of your driver to use your functions. The sample driver could be a good point to get started, just replace the "printf" lines with actually sending a command to the cutter. https://github.com/t-oster/LibLaserCut/blob/master/src/com/t_oster/liblasercut/drivers/SampleDriver.java#L63

tatarize commented 5 years ago

He's actually really proud of it.

The format is generally built so that he could run it only the cheapest oldest embedded processor. And it's kinda based on plotter. D means down (turn laser on), U means up (turn laser off). He screwed up RLTB because they go in the wrong directions. Runs a pulse tick counter for deciding the time between stepper motor pulses, which controls the speed (and adds some ticks when it triggers both stepper motors). And the lowercase distance values add magnitude to the right/left and top/bottom amounts which is then gets assigned to whatever direction was last issued.

Some of the design choices are pretty smart if you want the dumbest controller ever. He wanted to outsource all the higher level stuff to the software. It basically does no work at all. Which means he doesn't need a processor fast enough to interpret g-code or whatever.

The usb4java code wraps up the libusb stuff pretty nicely and lets java access the USB. My project already does that stuff. So it's brung. Though it would add a few requirements to version of this project that included the driver.

None of that seems pretty hard, the USB driver bit seems like it might either be a sticking point or something you folks should add anyhow, there's plenty of USB connected devices. And a lot of lasers have stuff like send the Ruida this data to that endpoint etc.

tatarize commented 5 years ago

I have most of the core elements down and sort of working. And I think this is the coherent way to write that driver. I might still have critical bugs and it might not work out of the box just yet.

https://github.com/tatarize/LibLaserCut

It needs some more testing, but it seems to build usable LHYMICRO-GL code and sends LHYMICRO-GL code to the device. Still really rough and I likely missed a spot or two. And it lacks some clear support for some features that do exist. I wasn't sure where to hook up the speed control, or whether it needed speed/power to fake the heat input.

It should properly have a setting for board, namely A/B/B1, M/M1, B2, and M2. These encode slightly different speed values and speed code syntax. M2 is the most popular.

The boards do have a raster feature. It should likely support that, but I'm not sure where to put that in. Rasters also have a couple settings like space between scanlines, might be part of JobPart though.

I added in a MockUsb class in the driver so you can test it even without a device. Just switch the K40Usb for the MockUsb in the K40Queue class. I might have Mock currently hooked up.

tatarize commented 5 years ago

Okay, fixed the rest. It should work. Though, I haven't gold-standard tested it yet (actually had it fire the laser).

Still needs some added features.

I don't know the ins and outs of the raster stuff. I don't know what the speed and power range of the laser stuff does. What are the units of the speed 100 it sends it? Is that a percent of the fastest speed or something? Why does it say power 0? Is that correct, wouldn't that turn it off?

tatarize commented 5 years ago

It connects to the K40 and starts controlling the device. However, it still needs debugging. It's losing position somehow, the default speed is too fast. The laser doesn't correctly update the state to off when it leaves cut-mode and thus doesn't turn it back on. And I dunno about the positioning. It's moving weirdly. Maybe cut in job is relative rather than absolute. Dunno. But, the ability to control the laser with LibLaserCut, even while I'm apparently doing it wrong.

tatarize commented 5 years ago

Okay, with my last update for now.

It connects to the device, works fairly well. It messes up the positioning somewhere. Gotta hunt that bug down. But, for many things it works almost okay. Like it can engrave or cut a circle without much trouble. Starts to notice the last bug if it's doing a bunch of letters. I'm still not sure how to enter the speed. I just have it taking the speed attribute and calling that mm/s. It's apparently a percent or something but I dunno any units and the stupid laser goes 240 mm/s it's just crazy to run it that fast.

Not really sure what else would be needed beyond tracking down that bug. And some features like raster getting put in.

t-oster commented 5 years ago

Thanks for all your work. I hope we can make this driver production ready soon.

mgmax commented 5 years ago

I don't know the ins and outs of the raster stuff.

Just "iterate" over the lines in the raster part. Every line is an array of bytes, every bit means 0 = don't laser, 1 = do laser. https://github.com/t-oster/LibLaserCut/blob/master/src/com/t_oster/liblasercut/RasterPart.java#L55-L76

A starting point could be: https://github.com/t-oster/LibLaserCut/blob/master/src/com/t_oster/liblasercut/drivers/EpilogCutter.java#L584

I don't know what the speed and power range of the laser stuff does. What are the units of the speed 100 it sends it? Is that a percent of the fastest speed or something?

Most drivers use PowerSpeedFocusProperty, which supports integer values from 0 to 100 for speed and power. It is intended to be "percent of maximum". If integers are not fine enough, there's also a Float implementation of the same class. You can also implement a different property class for your driver, if you'd prefer physical units.

If you don't use the power setting, then just ignore it. In the future, you could extend PowerSpeedFocusProperty to optionally hide the power setting. (We assumed that all lasers support that setting.) https://github.com/t-oster/LibLaserCut/blob/master/src/com/t_oster/liblasercut/PowerSpeedFocusProperty.java

You should switch off the laser at MOVETO commands, and switch on at LINETO.

Why does it say power 0? Is that correct, wouldn't that turn it off?

The default setting for newly created materials is "power 0, some speed". This is to avoid accidentally burning your material if you forgot to set a laser setting. Most lasercutters will use the absolute minimum of laser power (almost zero) if the power is set to zero.

Good luck with your development! My suggestion would be that you create a pull request as soon you consider your driver good enough for testing by "non-developers".

tatarize commented 5 years ago

Okay. The driver works. Or rather seems to have no extremely pressing flaws. You can totally test it if you have a k40 without much trouble. Found the error that was corrupting the flow. It was in my distance routine that said "TODO: recheck this". You won't necessarily be wasting wood to try it.

There isn't a 100% speed on the K40. There's speed code that so fast it rips apart, or sounds like it's trying. It feeds pulses into the stepper motor about as fast as you tell it to. But, this gives a proper speed in whatever units you want. And I spent a long time actually timing the damned thing.

Couple other questions. Do you have anywhere to shove commands for homing the device? Or nudging it over? Firing the laser while pointlessly stationary? Unlocking the rail bar? I guess the only big thing that actual developers won't be able to figure out without deep knowledge of the stock controller is the Raster bit. I'll submit the pull but work on that.

tatarize commented 5 years ago

Okay, hit a snag. The raster thing still confused me. It's likely an error dealing with not knowing the code there. I previously, as a project, wrote a PNG parser that directly parsed PNG pixels and then turned those pixels into either cut or no-cut commands and figured this would be like that.

Maybe I'm using the commands wrongly, but it just cut back and forth for a bit, then started only cutting to the right. Am I being given the raw pixels? When it says there's 8 bytes those are 1 means cut 0 means for 8 pixels in a row right?

I see that even when I specifically say I have no power option it called it for raster anyway to do something even though I said I didn't have one. I had to remove a valid throw error to get it to run.

I have the wherewithal to spelunk through the code but I figured I'd ask.

   if (p instanceof RasterPart)
      {
        RasterPart rp = (RasterPart) p;
        ArrayList<Byte> list = new ArrayList<Byte>();
        device.raster_start();
        int i = 0;
        try
        {
          while (true)
          {
            rp.getRasterLine(i, list);
            device.scanline_raster(list, (i & 1) == 1);
            i++;
          }
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
          //Out of scanlines
        }
        device.raster_end();
      }

Naively, my functions. raster start(), preps the raster putting the device into raster mode. scanline_raster() takes a list of byte which is read right to left, most significant digit. But, also takes a parameter to reverse the line, going left to right starting with the least significant digits. It flips the way it reads each. It assumes each line exists and is the same number of pixels across. Like this are DAT elements within an PNG. and raster_end() leaves raster mode, which basically exits the compactified mode.

tatarize commented 5 years ago

Also, another thing I've noticed is even when it works correctly it wrongly assumes the K40 has memory enough to store the project. It doesn't. It has a tiny buffer that stores a few packets. Literally after the job is finished. Which required sending all the specific information to the device and finishes sending a tiny bit before the job itself finishes. It then says to close the lid and make sure the ventilation is good. I dunno if that is cosmetic or if this driver is more alien than I think.

mgmax commented 5 years ago

The default message "Close the lid and press start etc." is more suited towards drivers which don't instantly start. More or less all other boards have some kind of "print queue" in the machine. You can edit this message in your laser settings. If you want the message to be shown before the job is sent, this would need a new attribute in LaserCutter to distinguish whether the lasercutter immediately starts or not, and then some changes in VisiCut to handle this.

What you write regarding rastering sounds like you understood it correctly. You could also just call RasterizableToVectorPart() to convert the raster data into LINETO/MOVETO commands.

If you are unsure if the pixels are right, just add some print() code to print black or white squares (unicode: □ ■ ), so you have a debug output.

"it just cut back and forth for a bit, then started only cutting to the right" sounds like a bug in your code, I don't see how this could be caused by anything outside of raster_scanline(). You could test that by calling raster_scanline with a known byte array.

My experience is that it's helpful to implement saveJob() to save jobs to a file, and write a dump utility which reads the laser files. In util/ we already have two of these for Epilog and LTT. This way, you don't need the real machine for most of the testing.

tatarize commented 5 years ago

The raster mode in the board isn't strictly lineto, moveto commands. It has a thing where it goes faster and makes the steps better by forcibly making a step of raster_step in the last flagged vertical direction when the horizontal direction changes. If you replace that entire routine with just move and lines it does a horrible clicking at the line ends.

However, that RasterableToVectorPart() routine is great sample code for how that should be done and I'll just snag it and hopefully just have it correct any oversights on my part.

Rather than implementing saveJob(), my driver has a checkbox that replaces the USB driver with a MockUsb driver that just sort of waits and prints the packets to the screen. Which was, for a time, a more important debugging method. The obviously most reasonable saveJob() format would be to write a fully qualified .egv file, but that would mean actually implementing the reset methodology and the modes for that method.

Basically it runs in compact mode, and there's two ways to exit compact mode. Either you reset '@' or you finish 'F' (three really as you can exit with just 'N' but then you can't set a different speed). You can issue more direct instant effect commands if you call I 'S1P' and pad the packet. But, the I command interrupts which means if it was still doing something it stopped doing that and lost the queue. Which is something I can do if I know it's finished. If I just append these commands on the end they only execute when the packet is sent and I can't send an unfilled packet so these instant commands might need to wait. Though I could jump into compact mode, do nothing and exit with a F, if these commands are needed. There's likely better methods for this stuff but I didn't find any yet.

As is though, I don't even exit compact mode unless the speed changes. Not even for the moveto's. Which is something people will notice eventually. "Hey, why did it finish doing that cut at 3mm/s then move to the next really far area at the same superslow speed without cutting anything when it could have just moved a lot faster than that?"

tatarize commented 5 years ago

I'm done.


It seems to work, I can raster engrave and cut and mark stuff without issue. And the whole thing seems to do larger projects without any hiccups so far. Feel free to modify the driver, however you see fit. If technical stuff comes up and needs dealing with, I'll look into that. How it interfaces with the rest of the project was a hassle for me. If other settings would be better, go ahead and do that.

I'll go back to messing with Jk40 (https://github.com/K40Nano/Jk40). Or one of my embroidery projects. Nothing more to do here until feedback comes in.

t-oster commented 5 years ago

Well since this is a new driver, it should not break anything. I @mgmax has no objections I will merge this so we get a larger test-user base soon