terjeio / grblHAL

This repo has moved to a new home https://github.com/grblHAL
231 stars 90 forks source link

Support RS485 control of Huanyang VFD (chinese spindles) #68

Closed Harvie closed 2 years ago

Harvie commented 4 years ago

Hello, can you please add support for this?

http://fightpc.blogspot.com/2014/10/vfd-control-with-arduino-using-rs485.html

Harvie commented 3 years ago

BTW what does Pn:R mean? I don't remember ever seeing this in classic GRBL. eg.:

<Alarm|MPos:0.000,0.000,0.000|Bf:35,1022|FS:0,0|Pn:R>

update: sorry, seems to be just mess in my wiring...

terjeio commented 3 years ago

Can you try with increasing the RX timeout, if the VFD is slow to respond then alarm 14 is raised:

https://github.com/terjeio/grblHAL/blob/61f09cd87cc5ec16fdf1711b90b76ea516ae3d15/drivers/ESP32/driver.c#L1574-L1577

BTW what does Pn:R mean? I don't remember ever seeing this in classic GRBL.

It means the grbl reset pin is asserted - grblHAL defaults to normally closed switches for safety reasons (unlike grbl). Invert the input with $14=1 to get rid of it (or short the reset pin to ground).

phil-barrett commented 3 years ago

3.3V ESP32 might be even more prone to such issues.

Definitely true. especially if using NO inputs. (NC are a little better, btw) That is why I am making 12V an option for the control inputs on my next board design. Much more resilient to EMI.

Harvie commented 3 years ago

Another thing i noticed:

0.) spindle running at eg. 2700 RPM 1.) i send for example S3000 and spindle does not do anything 2.) i send S3000 again and grbl responds with "ok", but does not re-send the S3000 to spindle, because it thinks that spindle is already at 3000 RPM even when it somehow missed the message in 1.) 3.) i can send S3000 how many times i want and grbl replies with OK, until i change to lets say S3200, then whole cycle repeats (based on whether or not the spindle got the message.)

Can you try with increasing the RX timeout, if the VFD is slow to respond then alarm 14 is raised:

OK i will try that

Harvie commented 3 years ago

Can you try with increasing the RX timeout

Tried rx_timeout = 400 and initialy it felt bit better. first 3 commands worked, but then the problems rised again... Also tried rx_timeout=100 and it feels same as with original 40...

Harvie commented 3 years ago

I tried shorter RS485 cable and baudrate of 4800 (lowest possible setting on VFD) and things seem even worse...

Harvie commented 3 years ago

Another thing i consider weird is that 1 or 2 commands after GRBL reset works and then something bad happens. Maybe there are some partialy received messages in buffer screwing all subsequent communication?

Harvie commented 3 years ago

Just for reference, i am using following setup:

image

image

Except the IO pin numbers of esp32 are different (reflecting the *map.h file). Also note that RE pin is inverted, so if you tie DE and RE together, you either transmit or receive based on the voltage level present.

There might be several issues with this:

1.) i am not sure if MAX485 can use 3.3V (TTL both for Vcc and IO) 2.) i am not sure whether it's ok that i don't have any shielding. I just use ~50cm long twisted pair pulled out of UTP cable. Such unshielded cable was working for me when using dedicated USB-RS485 adapter, so i expect that to be OK.

phil-barrett commented 3 years ago

Max485 can run on 3.3V but you might need to change the differential resistor (Rt on the Max485 datasheet).

Harvie commented 3 years ago

Max485 can run on 3.3V but you might need to change the differential resistor (Rt on the Max485 datasheet).

Thanks for the tip. I will certainly look into this!

phil-barrett commented 3 years ago

I should have been clearer. The MAX485 is spec'd to run on 5V. There are people that run it on 3.3V. Somewhere (i.e. I do not remember where) I saw a discussion of 3.3V operation. I think (do not hold me to this) that Rt needed to be lower by the ratio of 3.3/5. Also, both receiver and transmitter needed to be at the same voltage.

Harvie commented 3 years ago

You might need to change the differential resistor (Rt on the Max485 datasheet)

Currently there is 120 ohm resistance when measured across the output pins of the module

Also, both receiver and transmitter needed to be at the same voltage.

Ah. That might be the problem as i have no plans to modify the VFD to use 3.3V... However i can run the MAX485 on 5V and use 1K to 10K resistor as protection for ESP32 rx input pin. Hopefully the 3.3V TX coming from ESP32 will be enough to drive input of MAX485 running at 5V.

phil-barrett commented 3 years ago

However i can run the MAX485 on 5V and use 1K to 10K resistor as protection for ESP32 rx input pin. Hopefully the 3.3V TX coming from ESP32 will be enough to drive input of MAX485 running at 5V.

RX - I would use a 510/1000 divider. A 470/1000 divider will work at a slightly higher output voltage but still ok. [edit] better a 1K/2K divider[/edit] TX - the datasheet says Vih min is 2.0V so you should be good.

terjeio commented 3 years ago

2.) i send S3000 again and grbl responds with "ok", but does not re-send the S3000 to spindle, because it thinks that spindle is already at 3000 RPM even when it somehow missed the message in 1.)

It is coded that way, IMO better exception handling is required. There is two basic types of exception, timeout and exception reply from the VFD. Both result in alarm 14. I wonder what kind of exceptions you are getting. To see that add a message report for the exception code:

static void rx_exception (uint8_t code)
{
    report_message(uitoa(code), Message_Warning);
    system_raise_alarm(Alarm_Spindle);
}

in huanyang.c. 0 is timeout, I have to look up any other codes in the ModBus spec.

Another thing i consider weird is that 1 or 2 commands after GRBL reset works and then something bad happens. Maybe there are some partialy received messages in buffer screwing all subsequent communication?

If so the stream->flush_rx_buffer() call is not working. It is called before every message sent. Unlikely?

Harvie commented 3 years ago

To see that add a message report for the exception code:

Ok, i will test that, but i am not near the machine now...

@phil-barrett btw what about shielding? is that required for RS485 cabling?

phil-barrett commented 3 years ago

what about shielding?

Can't hurt but twisted pairs and differential signaling should be fairly noise resistant to start with.

terjeio commented 3 years ago

Come to think of one possible reason behind the problems. You are using the direction pin, this has to be changed when all the data has left the UART shift register, not only the FIFO as I do now. I have to dig into the datasheet to see if I can find a way to be certain all bits are on the wire before switching the direction pin (or add a minimum delay after the transmit FIFO is empty). IIRC the iMXRT1062 sets a flag when transmission is complete, perhaps the ESP32 has one too.

terjeio commented 3 years ago

...perhaps the ESP32 has one too.

There is a flag and an related interrupt too so for precise timing of the direction signal I am going to set it in the UART driver when a message is sent, enable the interrupt and reset it in the interrupt handler. It looks good on my scope, no randomness due to polling anymore. Will commit tomorrow.

Harvie commented 3 years ago

You are using the direction pin

I beleive there is no way to use bidirectional RS485 communication without using direction pin. You can get away without RX enable pin and supress loopback in ESP32 instead, which gives you oportunity to detect collisions. However you would still need properly timed direction signal for TX enable unless you are the only transmitter on the bus.

I am going to set it in the UART driver when a message is sent, enable the interrupt and reset it in the interrupt handler.

Can't the ESP32 UART hardware flow control be configured with direction (RTS) pin number to handle this for you, so it does not have to be handled in software?

See the mentions of "flow control" and "RTS" in this article: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#overview-of-rs485-specific-communication-options

terjeio commented 3 years ago

I beleive there is no way to use bidirectional RS485 communication without using direction pin.

Well, it is working for me with the chinese tranceivers I bought from ebay - unmarked chips though... With auto direction detect. The Rx/Tx LEDs has been happily blinking for many hours now while talking to the simulator. No alarms raised.

Can't the ESP32 UART hardware flow control be configured with direction (RTS) pin number to handle this for you, so it does not have to be handled in software?

It can, but requires a bit more digging into the datasheet? Pin assignment restrictions apply? Easy way first to see if this solves the issue.

Here are the changed files:

GrblDriver.zip

Harvie commented 3 years ago

With auto direction detect.

With auto direction you have to use some kind of null byte supression/escaping in your protocol.

It can, but requires a bit more digging into the datasheet? Pin assignment restrictions apply?

I don't really think this is the case as esp32 has means to internaly reroute the peripherals to different pins. Actually the only thing i do is checking pin numbers against these tables: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/

Here are the changed files

Thanks, i will hopefully try that this evening...

Harvie commented 3 years ago

I've managed to hunt down the evidence of problem in the original code:

image

So i guess i can compare it with the new code...

terjeio commented 3 years ago

I've managed to hunt down the evidence of problem in the original code:

Yep, this is from not waiting until the shift register is empty. I have updated the serial2txCount() function to add 1 to the count if transmit is not complete, this in addition to flipping the direction pin in the uart driver code.

Harvie commented 3 years ago

After update i am unable to find such issue in the waveform, i will try it with the real spindle later today:

image

Harvie commented 3 years ago

Here are the changed files: GrblDriver.zip

Just tested on the machine and so far not a single issue

Harvie commented 3 years ago

There is some weird rounding going on... But that's rather minor issue. Eg.:

M3 S17000    -> 16999 on VFD LCD
M3 S17000.3  -> 16999 on VFD LCD
M3 S17000.4  -> 17000 on VFD LCD
M3 S17000.9  -> 17000 on VFD LCD
M3 S17001    -> 17001 on VFD LCD

Except for this, i would consider the code production ready...

terjeio commented 3 years ago

Thanks for your effort!

There is some weird rounding going on... But that's rather minor issue.

Nothing can be done about that?

        uint16_t data = (uint16_t)(rpm * 100 / 60); // send Hz * 10  (Ex:1500 RPM = 25Hz .... Send 2500)

        rpm_cmd.adu[1] = ModBus_WriteCoil;
        rpm_cmd.adu[2] = 0x02;
        rpm_cmd.adu[3] = data >> 8;
        rpm_cmd.adu[4] = data & 0xFF;

With auto direction you have to use some kind of null byte supression/escaping in your protocol.

It does not seem to be required for the transceiver I've got. There are chips supporting auto direction control, e.g. MAX13410E, one would assume these do not require any protocol modifications. With auto direction the direction pin can be used for other purposes, important for the ESP32 since it has a limited number of pins available.

Except for this, i would consider the code production ready...

IMO the code could be improved a bit. E.g. now I poll the VFD on every real time report (typ. every 200ms), if a valid reply is not received then what? Currently I raise alarm 14, is this how it should be? Should there be retries when status changes are requested and the spindle does not respond? A $-setting for baud rate?

For the ESP32 options for baud rate (if not implemented as a $-setting) and auto direction should be added to CMakeLists.txt.

Harvie commented 3 years ago

For ther record, i've been using cheapest esp32 devkit, connected to cheapest max485 module using dupont cabling, with everything running at 3.3V, no shielding on RS485 data lines, just twisted wire (except for spindle 3-phase AC power wiring which absolutely has to be shielded). Didn't tested long cabling, but as far as ESP32 is in reasonable distance from VFD it should work. No 5V needed.

image

For the future i have plans to use this ESP32 based arduino uno compatible board, because my machine currently uses protoneer cnc shield with arduino uno and grbl right now. So i guess this will be easy to replace the uC unit this way while being easy to rollback in case of probles:

image

image

It even seems to have 3 extra pins (near the bottom on photo) which can probably be used for RS485...

https://github.com/terjeio/grblHAL/pull/159

Harvie commented 3 years ago

if a valid reply is not received then what? Currently I raise alarm 14, is this how it should be?

It's not really a defined anywhere. I guess when spindle fails to start, we should probably not crash stalled endmill into the material, so it probably makes sense to stop the g-code execution in such case. But i would strongly suggest to make this runtime configurable, because sometimes i want to override the spindle VFD by setting it to manual instead of rs485 mode... for whatever reason... Or simply run the machine without VFD powered on.

I guess we can have some integer number to indicate number of retries before alarm, eg.:

0 - never fail 1 - fail immediately after 1 failed attempt 42 - fail after 42 failed attempts

A $-setting for baud rate?

Certainly good idea... The VFD itself has only 4 options: 4800, 9600, 19200, 34800 But i guess being able to enter any arbitrary baudrate would be more future proof.

Harvie commented 3 years ago

IMO the code could be improved a bit.

BTW it is questionable whether we should use the hardware flow control (RTS) to switch the transciever direction or handle the direction pin manualy. It takes some CPU cycles to do that manualy and might not have perfect timing. On the other hand if handled manualy it might be easier to port the code to other platforms (eg. STM32 or something with more GPIO pins).

now I poll the VFD on every real time report (typ. every 200ms)

Would it make sense to make the polling interval configurable? Are there any possible benefits? Currently it is not completely clear to me why we even poll... You have to set the RPM and then poll the VFD to see if it got correctly configured, because it does not confirm the RPM directly when it get set? Probaly 200ms is reasonable and there is no need to configure this.

update: oh the realtime report polling is probably configurable at the sender side, so no need to have such thing...

terjeio commented 3 years ago

BTW it is questionable whether we should use the hardware flow control (RTS) to switch the transciever direction or handle the direction pin manualy. It takes some CPU cycles to do that manualy and might not have perfect timing. On the other hand if handled manualy it might be easier to port the code to other platforms (eg. STM32 or something with more GPIO pins).

I leave it up the driver implementation as this is handling the UART communication. The driver calls modbus_init() with a pointer to a structure holding pointers to the UART functions to use, one of these is set_direction, this is optional (could be NULL). If set then the plugin will call this function to set the direction pin as needed, e.g:

if(stream->set_direction)
    stream->set_direction(true);

So a driver implementer can choose how to handle this, either in the UART code or by providing a function to handle the pin manually. BTW porting the plugin code is not neccesary, it can be used for all processors if the driver has the required entry points (functions) implemented.

Would it make sense to make the polling interval configurable? Are there any possible benefits?

Not that I can think of, and it would only complicate the code. BTW it is kind of configurable already, by setting the sender polling rate for the real-time report.

You have to set the RPM and then poll the VFD to see if it got correctly configured, because it does not confirm the RPM directly when it get set?

If the message setting the RPM is accepted one could assume the spindle is running at the requested speed... grblHAL has implemented functionality for "spindle at speed" to ensure this. This requires either an encoder connected to the spindle or some other way to get the actual RPM. IMO a good VFD should return the actual RPM when queried, not the programmed one - if so this can be used as input to the "spindle at speed" algorithm.

Harvie commented 3 years ago

IMO a good VFD should return the actual RPM when queried, not the programmed one

Idealy it would be able to return both... the VFD has factory preset 20 second acceleration. That would mean you can only confirm that speed is correctly set after waiting for 20 seconds...

This requires either an encoder connected to the spindle or some other way to get the actual RPM.

BTW. Have you considered plugin to do closed loop PID control of spindle? To drive spindle using RPM sensor (eg. 1 pulse per revolution) and PWM output (increasing PWM duty cycle when RPMs start dropping under load?). For me this would be preferred for smaller machines, where it would be overkill to use VFD. This is also very popular spindle setup in hobby CNC world:

1.) low voltage DC motor based spindles with PWM can be easily retrofited with RPM sensor image

2.) cheap palm routers with brushed AC motors can be retrofited with RPM sensor and the PWM can be used to provide reference voltage for triac regulation. They usualy do it using another atmega, but i don't think this is really necessary, because grblHAL can easily handle it:

http://www.vhipe.com/product-private/SuperPID-Home.htm

https://www.youtube.com/watch?v=CmZL8IfQeDo

terjeio commented 3 years ago

Idealy it would be able to return both... the VFD has factory preset 20 second acceleration. That would mean you can only confirm that speed is correctly set after waiting for 20 seconds...

20 seconds must be for some really heavy motors (or high RPM ones?), you should be able to reduce that quite a bit?

BTW. Have you considered plugin to do closed loop PID control of spindle?

There is code in the MSP432 driver for that. However, I have decided against a plugin for spindle sync and I guess the same reasons are valid against a closed loop spindle control plugin. So copy/pasting code is the way to add closed loop control to more drivers. Some processor specific code is needed for getting the data for the RPM calculation too.

Harvie commented 3 years ago

I have decided against a plugin for spindle sync and I guess the same reasons are valid against a closed loop spindle control plugin

Can i know why? For example the esp32 has independent pulse counting hardware peripheral, which would probably handle the RPM readout quite elegantly without interferring with interrupts and other stuff... (not to mention it has two cores, so one might be dedicated to the motion control to make sure it remains smooth)

update: just found this: https://github.com/terjeio/grblHAL/issues/50 (but that do not explain much)

terjeio commented 3 years ago

Can i know why?

This is the main reason:

For example the esp32 has independent pulse counting hardware peripheral...

and then:

... which would probably handle the RPM readout quite elegantly without interferring with interrupts and other stuff...

which could be quite different from the interrupt based spindle sync implementations I have made. Spindle sync is a bit different though as this requires an index pulse in addition to the main encoder pulse train.


Each processor has different peripherals requiring HAL access if a plugins is to be made. For spindle sync and closed loop spindle RPM control this is perhaps half of the code needed. HAL overhead and lack of a RTOS are other reasons. So copy/paste of relatively few lines of code is the way to do it. To make it easier a template could be made.

What is readily available and handled by the core are settings for spindle pulses per revolution (PPR) and the PID values. These are enabled by setting a capability flag in the HAL structure. "Spindle at speed" likewise, a couple of code lines and setting a capability flag and the core takes care of the rest.

I am by no means negative to adding closed loop spindle control and would be happy if you want to give it a try, if it can result in a sensible plugin then I am all for that too.

Harvie commented 3 years ago

if it can result in a sensible plugin then I am all for that too.

fair enough :)

Do you need any help with cleaning up the rs485 plugin? I have plans to write some better documentation for the esp32 build process, so it's easier on beginners. And perhaps i will try to look into the arduino ide support...

terjeio commented 3 years ago

Do you need any help with cleaning up the rs485 plugin?

Too late, already done for the modbus plugin - settings added for baud rate (from a predefined list to reduce support due to misconfiguration) and RX timeout - and a hardcoded silent period after each message per ModBus spec (baud rate dependent). Switching the VFD on/off is a tad more complicated, have to think about that. A $-command?

I have plans to write some better documentation for the esp32 build process, so it's easier on beginners. And perhaps i will try to look into the arduino ide support...

That would be nice.

Harvie commented 3 years ago
   Nothing can be done about that?
   uint16_t data = (uint16_t)(rpm * 100 / 60); // send Hz * 10  (Ex:1500 RPM = 25Hz .... Send 2500)

I've been thinking about it. For example these are calculations i've made in bc:

scale=1
((17000*100 / 60)) * (60/100)
16999.9
((17000.5*100 / 60)) * (60/100)
17000.4

We can decode the frequency back to RPM (using the same algorithm as used in VFD) and if the floor() of the resulting value is not equal to our original RPM, the simply increment the frequency slightly.

This is very naive approach, but proves that this can be figured out.

This can probably be solved using some precision juggling magic. My gut feels like adding 0.5 to target RPM before calculation might do the trick... but i am not sure yet.

BTW does M3 Sxxx realy have to accept decimal numbers or it's just non-standart grbl extension?

terjeio commented 3 years ago

uint32_t data = lroundf(rpm * 100.0f / 60.0f); // send Hz * 10 (Ex:1500 RPM = 25Hz .... Send 2500)

is better, casting did not round - it behaves like floor. I was not aware of that.

We can decode the frequency back to RPM (using the same algorithm as used in VFD) and if the floor() of the resulting value is not equal to our original RPM, the simply increment the frequency slightly.

grblHAL adds the actual RPM as a third parameter to the FS: real time report element when a function for hal.spindle.get_data is provided by the driver or a plugin. The Huanyang plugin did provide any so I had to correct that. grblHAL reports the spindle RPM back as an int casted from float, I'll change that to use lroundf instead in the next build.

BTW does M3 Sxxx realy have to accept decimal numbers or it's just non-standart grbl extension?

Good question. NIST has this clue on page 94:

double GET_EXTERNAL_SPEED(); Return the system value for the spindle speed setting in revolutions per minute (rpm). The actual spindle speed may differ from this.

I have decided to use int for reporting in grblHAL and as the input field format in ioSender. float is still used internally in the core but not necessarily by all drivers and plugins. Reasoning behind that: 8-bit grbl uses PWM for RPM control, with an 8-bit timer for PWM too boot. Most (if not all) grblHAL drivers has at least 16 bit PWM. In both cases commanding exact RPMs with fractional values has no mapping to the real world, even with closed loop spindle control. A Huanyang VFD is similar as there are max. 2^16 discrete RPMs available.

How many grbl users have (or will get) a spindle that is possible to control precisely? And has a need for that?

Harvie commented 3 years ago

is better, casting did not round - it behaves like floor. I was not aware of that.

Well it depends on how bad the VFD firmware is. Maybe they have some issues with precision as well so that would mean we have to add opposite imprecission in our code. In the past i had horror experiences with chinese firmware, including precision problems (different devices, not HY VFDs), so nothing would really suprise me...

Anyway i will thest the suggested code and tell you what the results are...

A Huanyang VFD is similar as there are max. 2^16 discrete RPMs available.

That should easily map to 0-24000 RPM range of the most common spindles...

How many grbl users have (or will get) a spindle that is possible to control precisely? And has a need for that?

I highly doubt that deviation of 1 RPM will make any difference unless you are milling at speed of 5 RPMs :-) To be honest only reason why i am looking into this small deviation is my OCD, in practice i don't really care if i cut at 8000RPM or 7999RPM, but having both displays show the same value will certainly make this look more professional and trustworthy...

Harvie commented 3 years ago

Just tested...

This still gives me wrong RPM

#include <math.h>
...
uint32_t data = lroundf(rpm * 100.0f / 60.0f); // send Hz * 10 (Ex:1500 RPM = 25Hz .... Send 2500)

This seems to fix the issue:

uint16_t data = (uint16_t)((rpm+0.5) * 100 / 60); // send Hz * 10  (Ex:1500 RPM = 25Hz .... Send 2500)

I've been testing it for 10 minutes and i was unable to find RPM value which translates incorrectly.

terjeio commented 3 years ago

New commit made to the test branch. Settings for modbus baud rate and rx timeout added, use $help modbus for details.

The commit uses lroundf to fix the RPM issue, and requires lroundf in report.c to get back to original value. If this does not work as it should I'll take a look at it again with your proposal above.

Missing report of the actual RPM read from the spindle added to the real time report. Other issues fixed to, the main was hang on a soft reset. Enumeration data for the new settings added (to $ES and $EG), can be used by senders for the settings UI.

This still gives me wrong RPM

Where? In your OCD?

Harvie commented 3 years ago

Where? In your OCD?

Yes. I just issue M3 S8000, the VFD starts the spindle and after second or two it accelerates to 7999 RPMs. (VFD has its own display showing the RPMs). With the +0.5 hack it works very well, but i think we might add protection to ensure zero RPM translates to 0Hz. I am not even sure if that is needed, because the VFD itself has minimum RPM set to prevent stalling the motor. But just to be sure:

uint16_t data = rpm == 0 ? 0 : (uint16_t)((rpm+0.5) * 100 / 60); // send Hz * 10 (Ex:1500 RPM = 25Hz .... Send 2500)

Harvie commented 3 years ago

Switching the VFD on/off is a tad more complicated, have to think about that.

Idealy i'd like to be able to use modbus to control spindle and PWM output to control laser, so i can have both on single machine...

I can pull the plug of VFD so there's no need to actualy turn it off. I just need grbl to ignore the fact that VFD is not responding in order to prevent alarms during laser operation.

terjeio commented 3 years ago

Yes. I just issue M3 S8000, the VFD starts the spindle and after second or two it accelerates to 7999 RPMs.

Should be 7999.8, with your rounding it should be 8000.4. So there is some magic going on - imprecise PID or faked RPM in the VFD display? Wonder what the measured RPM is? The error is less with lroundf...

[edit] corrected wrong numbers above.

BTW PD003 can set the frequency in steps of 0.01Hz, commanded via ModBus only 1Hz.

I can pull the plug of VFD so there's no need to actualy turn it off.

A $-command or a setting (use the $32 setting for laser mode?) to switch mode? Reset of controller required? The VFD code "inserts" itself by claiming a number of HAL entry points, these can be restored back to the default PWM mode of operation. However, I do not like the idea of an unresponsive VDF triggering that automatically... Checking for laser mode on boot could be easy to implement, IMO not problematic as nobody would want to use a VFD to control a laser?

Harvie commented 3 years ago

So there is some magic going on - imprecise PID or faked RPM in the VFD display?

I don't know, but the code i've provided works well. To be honest... As long as it does work properly, i don't even care how "chinese rounding" in VFD works to save myself from that horror. They probably just truncate the resulting RPM in VFD instead of rounding, so the offset of 0.5 fixes that.

A $-command or a setting (use the $32 setting for laser mode?) to switch mode?

Yes, i guess the $32 might be a good fit.

Also i've been thinking about some magic tool number, so we can switch between spindle and laser using eg.: M6 T9999 to switch to the laser and any other tool number to switch back...

That way i would be able to laser-engrave and cut the piece in one g-code file... (as long as the laser focal point is lower than the endmill)

But the $32 is stored across grblhal reboots, so it makes sense as well...

Reset of controller required?

I don't think that's necessary...

IMO not problematic as nobody would want to use a VFD to control a laser?

I am not aware of any laser on hobby CNC market that can be driven by VFD :-)

terjeio commented 3 years ago

Also i've been thinking about some magic tool number, so we can switch between spindle and laser using eg.: M6 T9999 to switch to the laser and any other tool number to switch back...

Use $32 to switch - the plugin already subscribes to the settings changed event so that would be a "clean" way to do it. Tool change logic that is not standard is not something that I will add to grblHAL. You can of course write your own custom plugin though.

For the RPM issue I'll stick to lroundf as this fits better with the rounding in report.c - I am not going to change report.c to fit the way Huanyang do the rounding. There may be other VFDs behaving differently, perhaps even their v2 protocol does?

Harvie commented 3 years ago

Even LinuxCNC has some weird compensation for this in their code:

https://github.com/bebro/linuxcnc-huanyang-vfd/blob/emc2/hy_vfd.c#L205

There may be other VFDs behaving differently, perhaps even their v2 protocol does?

I doubt there will be some possible compatibility with other brands of VFDs in this protocol... Don't really know about the v2, perhaps we can find someone willing to test this?

terjeio commented 3 years ago

I think I have a solution to dual spindle. It will not work without additional code and/or a board file since pins has to be assigned for both spindle types. If reassigning pins dynamically then external hardware is required? If the driver/board map is capable of handling two spindles then hal.driver_cap.dual_spindle has to be set before huanyang_init() is called.

Note that there could be issues surfacing around switching spindle, likely so if switching is done while the active one is running. To be continued - I'll take a short break from coding.

Harvie commented 3 years ago

bdring grbl port also has different approach to VFD frequency calculation: https://github.com/bdring/Grbl_Esp32/pull/544/files#diff-7fed6b73a3c9ce8053c389e6c1c6eabbc507cce0fede33d2ff6269b3b25ce15eR68

uint16_t speed = (uint32_t(rpm) * 10000L) / uint32_t(max_rpm);