M150 during G29
-
@DonStauffer daemon.g
echo "DAEMON!" if global.MeshPointCount > 0 var X = move.axes[0].machinePosition var Y = move.axes[1].machinePosition + sensors.probes[0].offsets[1] var XMin = move.compensation.probeGrid.mins[0] var YMin = move.compensation.probeGrid.mins[1] var XMax = move.compensation.probeGrid.maxs[0] var XSpacing = move.compensation.probeGrid.spacings[0] var YSpacing = move.compensation.probeGrid.spacings[1] var PriorRowCount = (var.Y - var.YMin) / var.YSpacing var PointsPerRow = 1 + (var.XMax - var.XMin) / var.XSpacing var PriorRowPointCount = var.PriorRowCount * var.PointsPerRow var CurrRowPointCount = (var.X - var.XMin) / var.XSpacing if mod(var.PriorRowCount, 2) > 0.5 set var.CurrRowPointCount = (var.XMax - var.X) / var.XSpacing var PointCount = var.PriorRowPointCount + var.CurrRowPointCount + 0.5 echo "X=", {var.X}, " Y=", {var.Y}, " XMin=", {var.XMin}, " YMin=", {var.YMin}, " XMax=", {var.XMax} echo "XSpacing=", {var.XSpacing}, " YSpacing=", {var.YSpacing}, " PriorRowCount=", {var.PriorRowCount} echo "PointsPerRow=", {var.PointsPerRow}, " PriorRowPointCount=", {var.PriorRowPointCount}, " CurrRowPointCount=", {var.CurrRowPointCount} echo "daemon: ", {var.PointCount}, ", ", {var.PointCount / global.MeshPointCount} M98 P"/Macros/Lights/NeoPixel/Progress" R255 U255 B0 I32 C10 E0 F{var.PointCount / global.MeshPointCount} ;M99 else if global.JobStatus = "Printing" M98 P"/Macros/Lights/NeoPixel/Progress" R0 U255 B0 I32 C10 E0 F{job.rawExtrusion / global.JobFilament} ;M99 if global.JobStatus = "HeatingBed" var Fraction = (heat.heaters[0].current - global.JobTempBedStart) / (heat.heaters[0].active - global.JobTempBedStart) M98 P"/Macros/Lights/NeoPixel/Progress" R255 U0 B0 I32 C10 E0 F{var.Fraction} ;M99 ; Job Statuses defined so far: ; if global.JobStatus = "None" ; if global.JobStatus = "Starting" ; if global.JobStatus = "HeatingBed" ; if global.JobStatus = "Printing"
-
@DonStauffer Progress
(This is full of echo commands to see what's happening)
; Manage parameters and defaults var Red = 255 var Green = 255 var Blue = 255 var Intensity = 32 var LEDCount = 10 var StripNum = 0 var LEDNum = 0 var ProgressLEDCount = 0 if exists(param.R) set var.Red = param.R if exists(param.U) set var.Green = param.U if exists(param.B) set var.Blue = param.B if exists(param.I) set var.Intensity = param.I if exists(param.C) set var.LEDCount = param.C if exists(param.E) set var.StripNum = param.E if exists(param.F) echo "param.F='", {param.F},"'" set var.ProgressLEDCount = floor(param.F * var.LEDCount + 0.5) if global.ProgressLEDCount != var.ProgressLEDCount echo "A" set global.ProgressLEDCount = var.ProgressLEDCount echo "B" ; Set correct number of lit LEDs if var.ProgressLEDCount == var.LEDCount echo "C" M150 R{var.Red} U{var.Green} B{var.Blue} P{var.Intensity} S{var.ProgressLEDCount} F0 E{var.StripNum} echo "D" else echo "E" M150 R{var.Red} U{var.Green} B{var.Blue} P{var.Intensity} S{var.ProgressLEDCount} F1 E{var.StripNum} echo "F:", {var.LEDCount - var.ProgressLEDCount}, ", ", {var.StripNum} var DarkLEDCount = var.LEDCount - var.ProgressLEDCount echo "G" M150 P0 S{var.DarkLEDCount} F0 E{var.StripNum} echo "H"
-
@DonStauffer Known timing issue with this, which I haven't fixed yet since it's a work in progress. If you happen to run G29 when the daemon is about to run, it will catch the carriage on the way to the first point, and erroneously conclude it's something like 59% done. I'm not too concerned with this since I know why it happens. It's the hanging on M150 F0 I can't figure out.
-
@DonStauffer RepRapFirmware for Duet 2 WiFi/Ethernet version 3.4.6 (2023-07-21 14:08:28) running on Duet WiFi 1.02 or later + DueX5
-
@DonStauffer unless the port used by the M150 command supports direct DMA (e.g. the dedicated LED ports on the Duet 3 series main boards), M150 F1 has to disable interrupts while sending the LED data, in order to get the precise timing needed by Neopixel LEDs. This would mess up any movement commands; therefore if the port doesn't support direct DMA then M150 waits for motion to stop before executing. In your case, the M150 command will complete when the G29 command has finished.
I have updated the documentation for M150.
-
@DonStauffer I've added a note to the Connections > Dedicated LED connector > Duet 2 WiFi/Ethernet tab here: https://docs.duet3d.com/en/User_manual/Connecting_hardware/IO_Neopixel_DotStar#connections
Note that movement will be suspended any time M150 is used to update LEDs. So OK at the start/end of a print, or the end of heating up, but not a good idea during a print. This is because the CONN_LCD port doesn't have hardware DMA support for LEDs, the CPU has to stop all other activity including step pulse generation in order to generate the correct pulses to set the LEDs.
Ian
-
@dc42 I thought the CONN_LCD pin 5 on the Duet 2 WiFi did support NeoPixels - are you saying it doesn't?
-
@dc42 "Note that movement will be suspended any time M150 is used to update LEDs. So OK at the start/end of a print, or the end of heating up, but not a good idea during a print. This is because the CONN_LCD port doesn't have hardware DMA support for LEDs, the CPU has to stop all other activity including step pulse generation in order to generate the correct pulses to set the LEDs."
Had that been stated when I started this project 4 months ago I would not have attempted it. It makes NeoPixels useless not to be able to use them during a print, since the whole point is for them to be indicators of progress. I've invested dozens of hours on this project. I designed and printed a board with buffers, a 5V power supply, and connectors to install in the electronics box of my Railcore, a printed mount for the NeoPixels with an integrated connector backshell, ordered numerous parts, etc. This is a major disappointment if there's no way to make it work.
I did check at the beginning and read all the materials. It sounded like the Duet 2 WiFi CONN_LCD port was made for NeoPixels and the like. I considered that question carefully and read that part repeatedly and carefully.
-
@dc42 Is there some sort of driver available I could add to the Duet 2 or Duex 5 for it to hand off the task of providing the timing for the NeoPixels?
For instance, is there a way to use this driver, which can receive data via I2C?
https://learn.adafruit.com/adafruit-neodriver-i2c-to-neopixel-driver/overview
-
@DonStauffer You could connect an Arduino over a serial port, and have that control the LEDs. That's what people did in the olden days, before LEDs were supported in RRF.
The CONN_LCD port was originally for supporting LCDs, not LEDs, and for a long time Duet 2 didn't support LEDs directly (introduced in RRF 3.3) due to hardware limitations that @dc42 was eventually able to skirt around, but always with the caveat that motion stops (it's in the release notes for 3.3 under new features, but perhaps not well documented). As far as I'm aware, there are no available DMA pins on Duet 2. Another reason to move to Duet 3.
I don't know how to send messages over I2C, sorry. If there's a way, @dc42 would know!
Ian
-
@droftarts I do notice the Duex5 has a 10-pin "GPI & I2C Header", and the M260 command appears to be designed to use that. Arduino would be a new thing for me - I think I have one somewhere. But this Adafruit seesaw is only $7.50 and seems pretty capable (it also apparently handles the 5V pullup too). If it's just a matter of connecting the right pins from the Duex to the seesaw, that would be the easiest. The seesaw specifically is advertised for controlling NeoPixels (among other things). It's the connection to the Duex or Duet I'm uncertain about. Adafruit documentation:
SCL - I2C clock pin, connect to your microcontroller I2C clock line. There's a 10K pullup on this pin.
SDA - I2C data pin, connect to your microcontroller I2C data line. There's a 10K pullup on this pin.At a guess, maybe the Duex GPIO & I2C header "TWD" pin connects to the seesaw I2C clock pin, and "TWC" connects to the seesaw data pin, and I assume a ground wire would be needed too. Then the 5V supply and NeoPixels have obvious seesaw screw terminal connections.
Also I don't know anything about this; from Adafruit:
"We recommend using 100KHz I2C, but speeds of up to 400KHz are supported. You may want to decrease the SDA/SCL pullups to 2.2K from 10K in that case." I don't know Duex capabilities in this regard. I can handle adding resistors and the like.
Similarly, they make the comment:
"Now, to be fair - it's not super fast because we have to write each pixel over I2C, but with an 800KHz or 1MHz I2C clock and as long as you're not writing the whole strip at once, it's not so bad!"
I'm not sure what the Duex capabilities are in that regard either.The Seesaw default I2C address is 0x60, and M260 has the A parameter to specify that, and I'd just have to figure out what bytes to send, which seems to be documented by Adafruit. Apparently you start with the "standard I2C write header" (is that sent automatically by the M260?), then 2 register bytes (module base register address, which should be 0x60 I think for the seesaw, then module function register address, which seems to be 0x0E for the seesaw NeoPixels module), then just send the data bytes. Doesn't seem too hard.
Is there any obvious way this would not work? Not asking for a promise it WILL work, just 1. Does it seem reasonable, and 2. Is there any obvious pitfall? (in particular, any danger to the Duex board?)
It seems to me this is an inexpensive solution that should work. The driver supplies the signals the NeoPixels need, and the duet just basically uses I2C to "poke" values into the buffer of the seesaw chip in the driver. Assuming there are no limitations of the M260 like there are with M150, that should solve it.
-
@DonStauffer It seems to me the NeoPixels could then be controlled like this. I think setting the buffer size would probably only need to be done once.
; Function 0x03, set the buffer size, 3 bytes per LED, 10 LED strip M260 A0x60 B{0x0E, 0x03, 0, 0x1E} ; 0, 0x1E is little-endian for 30 ; Function 0x04, set the 2nd and 3rd LED blue: M260 A0x60 B{0x0E, 0x04, 0, 0x03, 0, 0, 0x20, 0, 0, 0x20} ; Start at byte offset 3 M260 A0x60 B{0x0E, 0x05} ; Function 0x05, Show
-
@DonStauffer correct, on Duet 2 you can use I2C to send/receive data to/from a device. Keep the I2C and the associated ground wires between the Duet and the I2C device short. Make sure that the I2C device either includes no pullup resistors and can handle 3.3V I2C signals, or has pullup resistors to 3.3V. If it doesn't satisfy this constraint, use a I2C level shifter.
-
@dc42 Would the same TTL buffer I got to run the NeoPixels off CONN_LCD pin 5 work to pull up I2C signals? It's an MC74VHCT50A hex buffer / CMOS Logic Level Shifter chip, with LSTTL-compatible inputs. It says "tPD = 3.5 ns (Typ) at Vcc=5V".
But I suspect the Adafruit device can do what's necessary. Their documentation says:
SCL - I2C clock pin, connect to your microcontroller I2C clock line. There's a 10K pullup on this pin.
SDA - I2C data pin, connect to your microcontroller I2C data line. There's a 10K pullup on this pin.I'm not sure what resistance is for what voltage. But also:
"To power the board, give it the same power as the logic level of your microcontroller - e.g. for a 3V microcontroller like a Feather RP2040 or most Single Board Computers, use 3V, or for a 5V microcontroller like Arduino, use 5V."
This Vin is independent of the 5V input used to output the neopixel control signal, so I guess I can take it from the Duet or Duex somewhere. Maybe the +3.3V pin from the Duex GPIO/I2C connector?
-
@DonStauffer that buffer won't work for I2C because it is unidirectional; but the Adafruit board sounds OK to connect directly.