Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add M118 Gcode support #3946

Merged
merged 4 commits into from Jul 13, 2023
Merged

Add M118 Gcode support #3946

merged 4 commits into from Jul 13, 2023

Conversation

RoboMagus
Copy link
Contributor

@gudnimg
Copy link
Collaborator

gudnimg commented Jan 24, 2023

Adding M118 only uses around 58 bytes of flash memory. 👍 Shorter than I expected. It looks like the E and A parameters come from Marlin and not Reprap wiki.

}

if (hasE) SERIAL_ECHO_START;
if (hasA) SERIAL_ECHO("//");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SERIAL_ECHOPGM("//") ... may be we even have this string already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not find occurence of "//" elsewhere, but changed to use macro SERIAL_ECHOPGM

Copy link
Collaborator

@DRracer DRracer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address my comments in the code, thank you.

@3d-gussner
Copy link
Collaborator

3d-gussner commented Jan 26, 2023

@RoboMagus Have you tested your PR?
I merged your PR locally and used the MK404 SIM with OctoPrint to review it.
If you are on Linux please use ./PF-build.sh -m1 to compile the firmware and start the MK404 SIM with this firmware.

Sending M118 A1 action:cancel (or any other action command) wasn't processed by OctoPrint. If I am not mistaken OctoPrint expects // action:<command> to trigger anything.

As MINI firmware is based on Marlin 2.x I would keep the syntax E1 and A1 as different favors of gcode are always a pain for Slicer developers. If we want to get any chance to get into Prusa Slicer we should not have differences here between MINI and MK2.5/3/S/+ Prusa printers.
Alternative would be to change E and A parameters in Marlin first and then here. I could imagine that they used the number as a delimiter in the string.

Please try to test PRs and add a short test log to the PRs, this helps a lot to review these.

Here my test logs:

2023-01-25 09:02:07,804 - Recv: echo: 3.13.0-ALPHA1-6894

2023-01-25 09:02:07,813 - Recv: echo: Last Updated: Jan 25 2023 09:01:27 | Author: (none, default config)
2023-01-25 09:02:07,816 - Recv: echo: Free Memory: 2516  PlannerBufferBytes: 1776


2023-01-25 09:04:18,341 - Send: M118 E1 Hello World
2023-01-25 09:04:18,351 - Recv: echo:M118 E1 Hello World


2023-01-25 09:05:20,069 - Send: M118 A1 action:cancel
2023-01-25 09:05:20,073 - Recv: echo:M118 A1 action:cancel

2023-01-25 09:07:18,781 - Send: M118 A1 action:pause
2023-01-25 09:07:18,784 - Recv: echo:M118 A1 action:pause

Here the log when pressing Pause in OctoPrint with some G-Cose scripts

2023-01-25 09:07:47,197 - Send: M601
2023-01-25 09:07:47,198 - Recv: ok
2023-01-25 09:07:47,199 - Changing monitoring state from "Pausing" to "Paused"
2023-01-25 09:07:47,201 - Recv: echo:enqueing "G1 E-1.000 F2700"
2023-01-25 09:07:47,202 - Recv: // action:paused
2023-01-25 09:07:47,202 - Printer signalled that it paused, switching state...

@RoboMagus
Copy link
Contributor Author

Apologies, I had only tested the code snippets separately as I do not have a 'spare' printer available at the moment to test on.
In testing I had not taken into account that the M118 is still part of the string pointed to by strchr_pointer.

I cannot get the MK404 sim to work, but with the offset added should work now.

@3d-gussner
Copy link
Collaborator

3d-gussner commented Jan 26, 2023

I cannot get the MK404 sim to work, but with the offset added should work now.

What are your problems with MK404? Are you on Linux? The ./MK404-build.sh script should guide you through the needed packages and then compile it correctly.

@RoboMagus
Copy link
Contributor Author

RoboMagus commented Jan 26, 2023

I'm using WSL and after working through all missing dependencies and disabling display I cannot get the Serial connection to work.

The script immediately exits:

No bootloader specified, or specified file not found. Skipping!
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
Failed to open audio: No available audio device
ScriptHost: NOTE: Duplicate context name (Thermistor) with different pointer. Incrementing ID...
ScriptHost: NOTE: Duplicate context name (Thermistor) with different pointer. Incrementing ID...
ScriptHost: NOTE: Duplicate context name (Thermistor) with different pointer. Incrementing ID...
ScriptHost: NOTE: Duplicate context name (Fan) with different pointer. Incrementing ID...
Creating pinspec for atmega2560
Prusa_MK3S_flash.bin: Read 262144 bytes.
Loading 262144 bytes of xflash
Loading 4096 bytes of EEPROM
Read 4096 bytes
Loaded 234976 bytes from HEX file: /mnt/c/Projects/Prusa-Firmware/../PF-build-hex/FW3130-ALPHA1-Build6054/BOARD_EINSY_1_0a/FW3130-ALPHA1-Build6054-1_75mm_MK3S-EINSy10a-E3Dv6full-EN_FARM.hex
Initialized VCD file Prusa_MK3S_VCD.vcd
uart_pty_init bridge on port *** /dev/pts/1 ***
/tmp/simavr-uart2 now points to: /dev/pts/1
note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal
uart_pty_init bridge on port *** /dev/pts/2 ***
uart_pty_init bridge on port *** /dev/pts/3 ***
SD file Prusa_MK3S_SDcard.bin does not exist. Will not create it.
SD card image (Prusa_MK3S_SDcard.bin) could not be mounted (error -1 ).
Init on ADC 0 start temp: 25
Init on ADC 2 start temp: 25
Init on ADC 3 start temp: 25
Init on ADC 6 start temp: 25
ScriptHost: NOTE: Duplicate context name (VSrc) with different pointer. Incrementing ID...
MK3S - adding IR sensor.
/tmp/simavr-uart0 now points to: /dev/pts/2
note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal
/tmp/simavr-uart1 now points to: /dev/pts/3
note: export SIMAVR_UART_XTERM=1 and install picocom to get a terminal
Validating script...
Script validation finished.
Waiting for board to finish...
Starting atmega2560 execution...
10M idle cycles is 346184 ns (0.346184 ns per tick)
atmega2560finished (2).
Wrote 262144 bytes of flash
Wrote 4096 bytes of EEPROM to Prusa_MK3S_eeprom.bin
Wrote 262145 bytes of xflash to Prusa_MK3S_xflash.bin
Stopping Einsy_atmega2560
Done
~uart_pty
~uart_pty
~uart_pty
Done

@3d-gussner
Copy link
Collaborator

3d-gussner commented Jan 26, 2023

WSL or WSL2? I heard that the new version works better. I used to use WSL and at the end I decided for dual boot and now barely use Windows.
Did you build it in WSl using ./MK404-build.sh ?

@RoboMagus
Copy link
Contributor Author

RoboMagus commented Jan 26, 2023

WSL2 indeed, which is better than nothing, but still causes too many LFS / Device related issues for my liking.
Meanwhile I do have the simulator running, but no UART interaction seems to be possible. (The issues seem to be related with my system and MK404 than with the changes in this PR)

That is after building with the MK404 build script.

I'm fairly confident though that with the latest commit the code in this PR the command should work as expected. Do you have the chance to check again if behavior is now as expected?

If not possible I'll hope to get to take some time during this weekend to flash the new FW on printer, as otherwise I could not test this myself.

@RoboMagus
Copy link
Contributor Author

Had one minor fix left ('M' also still part of strchr_pointer which I had not taken into account), but these changes have successfully tested on my Prusa MK3s together with Octoprint.

Here's a snippet from the Terminal:

[...]
Send: M118 Here is a test string that is echoed to Host from Prusa MK3s
Recv: Here is a test string that is echoed to Host from Prusa MK3s
Recv: ok
[...]
Send: M118 E1 Here is a test string that is ECHOED to Host from Prusa MK3s
Recv: echo:Here is a test string that is ECHOED to Host from Prusa MK3s
Recv: ok
[...]
Send: M118 A1 action:pause
Recv: //action:pause
Pausing on request of the printer...
Recv: ok
[...]
Send: M118 A1 E1 Here's a string with A1, E1
Recv: echo://Here's a string with A1, E1
Recv: ok
[...]
Send: M118 E1 E1 Here's a string with duplicate E1
Recv: echo:Here's a string with duplicate E1
Recv: ok
[...]
Send: M118 E1E2 Here's malformed command string 1
Recv: echo:E2 Here's malformed command string 1
Recv: ok
[...]
Send: M118 A2 Here's malformed command string 2
Recv: A2 Here's malformed command string 2
Recv: ok
[...]
Send: M118  A1 Here's malformed command string with an additional space before the parameter
Recv:  A1 Here's malformed command string with an additional space before the parameter
Recv: ok
[...]
Send: M118  E1 Here's malformed command string with an additional space before the parameter
Recv:  E1 Here's malformed command string with an additional space before the parameter
Recv: ok
[...]
Send: M118 E1 Here's malformed command string with an additional space before the parameter

@RoboMagus
Copy link
Contributor Author

RoboMagus commented Jan 30, 2023

@DRracer, After some additional testing with printing files containing M118 commands from SD card, I noticed that these lines are split (this is not the case when performing M118 over Serial).

This line appears to be the culprit, but I do not understand why a colon (:) would also be used as the start of comment in GCode. Cause any file I've seen thus far uses only semicolon (;) to indicate start of comment. This appears to be a bug.
Seeing as you authored this bit of code, could you clarify what's happening here please?

Edit:
Seems like this colon has been there since the code was ported from Marlin 1.x and appears to be the definition of legacy code. There was a bug filed for this back in 2015: Ticket by Foosel, and subsequently fixed in PR 1570.

I would propose to port these fixes also, as the behavior as it currently stands is not intended.

@DRracer
Copy link
Collaborator

DRracer commented Jan 31, 2023

@RoboMagus thank you for your effort getting your PR tuned and merged.

You are right, the : check is some kind of legacy code. Theoretically we could implement escaped sequences just like PR#1570 .

However, I'd like to avoid touching the G-code skipping state machine, it took us a lot of effort to get it up to the speed where it is now. That implies that escaping a semicolon ; will not be supported.

@RoboMagus
Copy link
Contributor Author

The colon issue should be fixed by PR #3978. This indeed does not add any escape sequence handling to avoid further complication of the GCode reading. I have not yet found any GCode that includes ; in any other way than just comments.

With that little bug squashed the M118 implementation is working as expected on my MK3s at least.

@RoboMagus
Copy link
Contributor Author

@DRracer, are these proposed changes OK to be merged?
Should include #3978 as well then.

@3d-gussner 3d-gussner added this to the FW 3.13.0 milestone Feb 10, 2023
@DRracer DRracer modified the milestones: FW 3.13.0-RC1, FW 3.13.0-RC2 Feb 17, 2023
@RoboMagus RoboMagus requested review from gudnimg and removed request for DRracer March 22, 2023 09:47
@gudnimg
Copy link
Collaborator

gudnimg commented Mar 24, 2023

@RoboMagus Sorry for the late review/response. But I'll try look at your PR over the weekend (and test on my printer). 👍 It looks good to me just by looking at the code but I want to test it on the hardware to understand the changes better.

@RoboMagus
Copy link
Contributor Author

Thanks for the update @gudnimg.
I figured it could be delayed as we're waiting for the next FW version before merging, but good to see its on the radar.

I've been running modified firmware on my MK3s for over a month with these changes incorporated (and have been extensively using with a W.I.P. Octoprint plugin) and have not ran into issues yet. Hope your testing yields the same results 😃.

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

Current memory changes:

Flash: +190 bytes
SRAM: 0 bytes

I wonder why it needs so much memory 🤔

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

I tried my best to make M118 fail but these results look good me. I think we should try to reduce the memory usage if we can. Note the timestamps below are just from Arduino IDE, not from firmware.

I noticed M118 will skip spaces in the beginning of the message:

M118 A1 E1 hello world
Yields:

11:05:41.865 -> echo://hello world
11:05:41.865 -> ok

Not sure if this is expected behavior.

Tests:
Input M118
Output:

10:46:04.530 -> 
10:46:04.530 -> ok

Input M118 A
Output:

10:47:32.066 -> A
10:47:32.066 -> ok

Input M118 E
Output:

10:48:05.096 -> E
10:48:05.096 -> ok

Input M118 A256
Output:

10:48:45.314 -> A256
10:48:45.314 -> ok

Input M118 A128
Output:

10:51:25.372 -> //28
10:51:25.372 -> ok

Input M118 A1 E192391932
Output:

10:52:07.666 -> echo://92391932
10:52:07.666 -> ok

Input: M118 A1 E1 Averyveryveryveryveryverylongstring
Output:

10:54:35.958 -> echo://Averyveryveryveryveryverylongstring
10:54:35.958 -> ok

Input M118 *
Output:

11:01:39.973 -> Error:No Line Number with checksum, Last Line: 0
11:01:39.973 -> Resend: 1
11:01:39.973 -> ok

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

Our new CMake builds also give us the .asm output. Here it is for this PR just to share it:

case 118: {
        bool hasE = false;
        bool hasA = false;
        char *p = strchr_pointer + 5;
   1a54e:	00 91 4c 03 	lds	r16, 0x034C	; 0x80034c <strchr_pointer>
   1a552:	10 91 4d 03 	lds	r17, 0x034D	; 0x80034d <strchr_pointer+0x1>
   1a556:	0b 5f       	subi	r16, 0xFB	; 251
   1a558:	1f 4f       	sbci	r17, 0xFF	; 255
   1a55a:	83 e0       	ldi	r24, 0x03	; 3
    - `E1` - Prepend echo: to the message. Some hosts will display echo messages differently when preceded by echo:.
    - `String` - Message string. If omitted, a blank line will be sent.
    */
    case 118: {
        bool hasE = false;
        bool hasA = false;
   1a55c:	f1 2c       	mov	r15, r1
    - `A1` - Prepend // to denote a comment or action command. Hosts like OctoPrint can interpret such commands to perform special actions. See your host’s documentation.
    - `E1` - Prepend echo: to the message. Some hosts will display echo messages differently when preceded by echo:.
    - `String` - Message string. If omitted, a blank line will be sent.
    */
    case 118: {
        bool hasE = false;
   1a55e:	40 e0       	ldi	r20, 0x00	; 0
   1a560:	81 50       	subi	r24, 0x01	; 1
        bool hasA = false;
        char *p = strchr_pointer + 5;
        
        for (uint8_t i = 2; i--;) {
   1a562:	e9 f0       	breq	.+58     	; 0x1a59e <process_commands() [clone .part.41]+0x3d7e>
          // A1, E1, and Pn are always parsed out
          if (!((p[0] == 'A' || p[0] == 'E') && p[1] == '1')) break;
   1a564:	d8 01       	movw	r26, r16
   1a566:	9c 91       	ld	r25, X
   1a568:	29 2f       	mov	r18, r25
   1a56a:	2b 7f       	andi	r18, 0xFB	; 251
   1a56c:	21 34       	cpi	r18, 0x41	; 65
   1a56e:	b9 f4       	brne	.+46     	; 0x1a59e <process_commands() [clone .part.41]+0x3d7e>
   1a570:	11 96       	adiw	r26, 0x01	; 1
   1a572:	2c 91       	ld	r18, X
   1a574:	21 33       	cpi	r18, 0x31	; 49
   1a576:	99 f4       	brne	.+38     	; 0x1a59e <process_commands() [clone .part.41]+0x3d7e>
          switch (p[0]) {
   1a578:	91 34       	cpi	r25, 0x41	; 65
   1a57a:	71 f0       	breq	.+28     	; 0x1a598 <process_commands() [clone .part.41]+0x3d78>
   1a57c:	95 34       	cpi	r25, 0x45	; 69
   1a57e:	09 f4       	brne	.+2      	; 0x1a582 <process_commands() [clone .part.41]+0x3d62>
            case 'A': hasA = true; break;
            case 'E': hasE = true; break;
   1a580:	41 e0       	ldi	r20, 0x01	; 1
          }
          p += 2;
   1a582:	98 01       	movw	r18, r16
   1a584:	2e 5f       	subi	r18, 0xFE	; 254
   1a586:	3f 4f       	sbci	r19, 0xFF	; 255
   1a588:	89 01       	movw	r16, r18
   1a58a:	2f 5f       	subi	r18, 0xFF	; 255
   1a58c:	3f 4f       	sbci	r19, 0xFF	; 255
          while (*p == ' ') ++p;
   1a58e:	f8 01       	movw	r30, r16
   1a590:	90 81       	ld	r25, Z
   1a592:	90 32       	cpi	r25, 0x20	; 32
   1a594:	c9 f3       	breq	.-14     	; 0x1a588 <process_commands() [clone .part.41]+0x3d68>
   1a596:	e4 cf       	rjmp	.-56     	; 0x1a560 <process_commands() [clone .part.41]+0x3d40>
        
        for (uint8_t i = 2; i--;) {
          // A1, E1, and Pn are always parsed out
          if (!((p[0] == 'A' || p[0] == 'E') && p[1] == '1')) break;
          switch (p[0]) {
            case 'A': hasA = true; break;
   1a598:	ff 24       	eor	r15, r15
   1a59a:	f3 94       	inc	r15
   1a59c:	f2 cf       	rjmp	.-28     	; 0x1a582 <process_commands() [clone .part.41]+0x3d62>
          }
          p += 2;
          while (*p == ' ') ++p;
        }

        if (hasE) SERIAL_ECHO_START;
   1a59e:	44 23       	and	r20, r20
   1a5a0:	21 f0       	breq	.+8      	; 0x1a5aa <process_commands() [clone .part.41]+0x3d8a>
   1a5a2:	8c ef       	ldi	r24, 0xFC	; 252
   1a5a4:	9b ea       	ldi	r25, 0xAB	; 171
   1a5a6:	0e 94 f6 89 	call	0x113ec	; 0x113ec <serialprintPGM(char const*)>
        if (hasA) SERIAL_ECHOPGM("//");
   1a5aa:	ff 20       	and	r15, r15
   1a5ac:	21 f0       	breq	.+8      	; 0x1a5b6 <process_commands() [clone .part.41]+0x3d96>
   1a5ae:	86 e2       	ldi	r24, 0x26	; 38
   1a5b0:	96 e8       	ldi	r25, 0x86	; 134
   1a5b2:	0e 94 f6 89 	call	0x113ec	; 0x113ec <serialprintPGM(char const*)>

        SERIAL_ECHOLN(p);
   1a5b6:	c8 01       	movw	r24, r16
   1a5b8:	0e 94 5a 94 	call	0x128b4	; 0x128b4 <MarlinSerial::println(char const*)>
   1a5bc:	0c 94 6e c4 	jmp	0x188dc	; 0x188dc <process_commands() [clone .part.41]+0x20bc>

@RoboMagus
Copy link
Contributor Author

Current memory changes:

Flash: +190 bytes SRAM: 0 bytes

I wonder why it needs so much memory 🤔

How do you find the increase in mem usage?
It's indeed strange that that's such a significant increase for such a relatively small addition. Especially since about 2 months ago this increase was MUCH smaller (your first comment):

Adding M118 only uses around 58 bytes of flash memory

All I can think of that's been changed in commits since then has been to use SERIAL_ECHOPGM i.s.o. SERIAL_ECHO 🤔.

@RoboMagus
Copy link
Contributor Author

I noticed M118 will skip spaces in the beginning of the message:
...
Not sure if this is expected behavior.

This behaviour is copied from the Marlin implementation to ensure behaviour does not deviate between implementations.

I cannot think of a usecase where spaces would be usefull to retain myself, but if there would be one it'd be best in my opinion to keep implementation consistent rather than deviate based edge usecases.

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

How do you find the increase in mem usage?

When I build the firmware via Vscode + CMake I get this result:

Keep in mind the number for the memory in Flash (label Program in the image) doesn't include the bootloader which eats up 8192 bytes of the free memory. As you can see we don't have that much memory free which is why I'm constantly looking for optimisations so we can squeeze more into the firmware 😅
image

I usually build the latest current MK3 branch, take a screenshot of the result. Then switch to the branch I'm working on and use my screenshot to compare with the result on my branch.

Note I am applying your changes to the latest MK3 branch, maybe something changed since you opened the PR which is increasing the code size but I don't see how.

I have found this alone takes 70B? hmm
char *p = strchr_pointer + 5;

I've also found one potential optimisation which may help in this PR. And that's to create a common function to skip whitespace such that:

while (*src == ' ') ++src; becomes src = cmd_skip_whitespace(src); I'm looking into it now.

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

I'm seeing this only is adding 70B 🤔 Strange the compiler doesn't optimize this away.
image

@gudnimg
Copy link
Collaborator

gudnimg commented Mar 26, 2023

It looks like the memory difference is due to the compiler or linker optimizing differently. So the issue is not your PR :) Probably some function is being inlined somewhere by LTO (not sure how to find it though, the asm file is huge).

@gudnimg gudnimg requested a review from 3d-gussner April 21, 2023 09:24
@RoboMagus
Copy link
Contributor Author

@DRracer / @3d-gussner
Is there still work required for this PR to be merged?

Copy link
Collaborator

@DRracer DRracer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now with some more space we can finally merge this PR.
The proposed changes are scope-limited.

Future improvements may include:

  • extracting M118 implementation away from Marlin_main.cpp (which may help the compiler to optimize the code better)
  • adding unit tests to see how malformed M118 records manage to break the whole thing

@DRracer DRracer merged commit 2d46157 into prusa3d:MK3 Jul 13, 2023
@DRracer
Copy link
Collaborator

DRracer commented Jul 13, 2023

@RoboMagus Thank you for keeping your PR up to date and responding to our reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants