Skip to content

feature: Detect board port change after upload#2253

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

Merged
merged 18 commits into from
Aug 18, 2023

Conversation

cmaglie
Copy link
Member

@cmagliecmaglie commented Aug 1, 2023

Please check if the PR fulfills these requirements

See how to contribute

  • The PR has no duplicates (please search among the Pull Requests
    before creating one)
  • The PR follows
    our contributing guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • UPGRADING.md has been updated with a migration guide (for breaking changes)
  • configuration.schema.json updated if new parameters are added.

What kind of change does this PR introduce?

The CLI is now able to detect the port where a board re-attaches after an upload (this is particularly useful for boards with native USB connections that require disconnection of the communication port to perform the sketch upload).
Previously this task was performed by the Arduino IDE with mixed results, for more info see #2245.

What is the current behavior?

After an upload if the board change port this is not detected.

What is the new behavior?

:~/Arduino/Blink$ arduino-cli upload -b arduino:samd:nano_33_iot -p /dev/ttyACM1 Atmel SMART device 0x10010005 found Device : ATSAMD21G18A Chip ID : 10010005 Version : v2.0 [Arduino:XYZ] Apr 19 2019 14:38:48 Address : 8192 Pages : 3968 Page Size : 64 bytes Total Size : 248KB Planes : 1 Lock Regions : 16 Locked : none Security : false Boot Flash : true BOD : true BOR : true Arduino : FAST_CHIP_ERASE Arduino : FAST_MULTI_PAGE_WRITE Arduino : CAN_CHECKSUM_MEMORY_BUFFER Erase flash done in 0.818 seconds Write 11404 bytes to flash (179 pages) [==============================] 100% (179/179 pages) done in 0.071 seconds Verify 11404 bytes of flash with checksum. Verify successful done in 0.009 seconds CPU reset. New upload port: /dev/ttyACM0 (serial) <------ 

The same information is available through JSON output too:

:~/Arduino/Blink$ arduino-cli upload -b arduino:samd:nano_33_iot -p /dev/ttyACM1 --format json { "stdout": "Atmel SMART device 0x10010005 found\nDevice : ATSAMD21G18A\nChip ID : 10010005\nVersion : v2.0 [Arduino:XYZ] Apr 19 2019 14:38:48\nAddress : 8192\nPages : 3968\nPage Size : 64 bytes\nTotal Size : 248KB\nPlanes : 1\nLock Regions : 16\nLocked : none\nSecurity : false\nBoot Flash : true\nBOD : true\nBOR : true\nArduino : FAST_CHIP_ERASE\nArduino : FAST_MULTI_PAGE_WRITE\nArduino : CAN_CHECKSUM_MEMORY_BUFFER\nErase flash\ndone in 0.819 seconds\n\nWrite 11404 bytes to flash (179 pages)\n\r[========== ] 35% (64/179 pages)\r[===================== ] 71% (128/179 pages)\r[==============================] 100% (179/179 pages)\ndone in 0.070 seconds\n\nVerify 11404 bytes of flash with checksum.\nVerify successful\ndone in 0.009 seconds\nCPU reset.\n", "stderr": "", "updated_upload_port": { "address": "/dev/ttyACM0", <------- "label": "/dev/ttyACM0", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x8057", "serialNumber": "2B59DD8F50534B54332E3120FF03113B", "vid": "0x2341" }, "hardware_id": "2B59DD8F50534B54332E3120FF03113B" } } 

Does this PR introduce a breaking change, and is titled accordingly?

Yes

Other information

Fix#2245

@cmagliecmaglie self-assigned this Aug 1, 2023
@cmagliecmaglie added priority: high Resolution is a high priority topic: code Related to content of the project itself topic: gRPC Related to the gRPC interface criticality: highest Of highest impact type: enhancement Proposed improvement labels Aug 1, 2023
kittaakos pushed a commit to kittaakos/arduino-ide that referenced this pull request Aug 2, 2023
 - update firmware uploader to `2.3.0`, - new gRPC API Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
@cmagliecmaglieforce-pushed the board_port_after_upload branch from 6a02899 to 2d5ec7fCompareAugust 3, 2023 13:59
@codecov
Copy link

codecovbot commented Aug 3, 2023

Codecov Report

Patch coverage: 58.26% and no project coverage change.

Comparison is base (246adf9) 62.94% compared to head (862bbe2) 62.94%.
Report is 3 commits behind head on master.

Additional details and impacted files
@@ Coverage Diff @@## master #2253 +/- ## ======================================== Coverage 62.94% 62.94% ======================================== Files 220 221 +1 Lines 19540 19724 +184 ======================================== + Hits 12299 12415 +116 - Misses 6151 6207 +56 - Partials 1090 1102 +12 
FlagCoverage Δ
unit62.94% <58.26%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files ChangedCoverage Δ
commands/daemon/daemon.go27.67% <0.00%> (-1.03%)⬇️
commands/upload/burnbootloader.go0.00% <0.00%> (ø)
commands/board/list.go58.70% <42.85%> (-1.50%)⬇️
internal/cli/upload/upload.go60.68% <53.33%> (+0.10%)⬆️
commands/upload/upload.go73.86% <63.19%> (-0.83%)⬇️
internal/cli/compile/compile.go73.43% <66.66%> (-0.54%)⬇️
arduino/discovery/discovery.go44.55% <69.23%> (+2.31%)⬆️
internal/algorithms/channels.go76.92% <76.92%> (ø)

... and 2 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@per1234per1234 left a comment

Choose a reason for hiding this comment

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

Arduino CLI should always return the port after an upload, even in the case where no port change is expected. The consumer shouldn't be required to implement "if not updated_upload_port, use original port" logic.

The whole point is that all the logic for determining which port should be selected after an upload should be implemented in Arduino CLI. The consumer should be able to simply select the port Arduino CLI tells it to select in all cases.

Arduino CLI should always return the port after an upload, even in the case where no port change is expected. The consumer shouldn't be required to implement "if not updated_upload_port, use original port" logic. The whole point is that all the logic for determining which port should be selected after an upload should be implemented in Arduino CLI. The consumer should be able to simply select the port Arduino CLI tells it to select in all cases.
@cmagliecmaglieforce-pushed the board_port_after_upload branch from 6b80bca to bfeefb5CompareAugust 9, 2023 12:59
@cmagliecmaglie marked this pull request as ready for review August 9, 2023 12:59
@cmagliecmaglie added this to the Arduino CLI 1.0 milestone Aug 9, 2023
@cmagliecmaglieforce-pushed the board_port_after_upload branch from bfeefb5 to 1b60c97CompareAugust 9, 2023 13:01
@cmagliecmaglieforce-pushed the board_port_after_upload branch from 1b60c97 to 990d93aCompareAugust 9, 2023 13:01
@cmaglie
Copy link
MemberAuthor

Arduino CLI should always return the port after an upload, even in the case where no port change is expected

I've implemented the behavior above.

@kittaakos
Copy link
Contributor

I received specific steps to force the port change with Uno R4 WiFi. In this case, the updated_upload_port does not tell the new port, but the port has changed.

I have a procedure you can use to get an address change on macOS (as well as Linux and Windows) using an UNO R4 WiFi board:

  1. Press the reset button on the board twice quickly. You should now see the "L" LED pulsing. The address will be something like /dev/cu.usbmodemDC5475C4C6542
  2. Upload the following sketch:
#include<HID.h>voidsetup() {} voidloop() {}

After the upload, the address will be something like /dev/cu.usbmodem2214101

Note that after you upload that sketch, the next attempt to upload a sketch to your board will fail (arduino/ArduinoCore-renesas#73). The workaround is to press the reset button twice quickly before uploading. The problem (and the address change) will no longer occur after you have successfully uploaded any other sketch that doesn't use HID to the board so performing the instructions I provided above doesn't do any permanent harm to the board.


Steps:

./arduino-cli version arduino-cli Version: git-snapshot Commit: 990d93a5 Date: 2023-08-10T14:51:50Z
./arduino-cli board list --format json
[ { "port": { "address": "/dev/cu.BLTH", "label": "/dev/cu.BLTH", "protocol": "serial", "protocol_label": "Serial Port" } }, { "port": { "address": "/dev/cu.Bluetooth-Incoming-Port", "label": "/dev/cu.Bluetooth-Incoming-Port", "protocol": "serial", "protocol_label": "Serial Port" } }, { "matching_boards": [ { "name": "Arduino UNO R4 WiFi", "fqbn": "arduino:renesas_uno:unor4wifi" } ], "port": { "address": "/dev/cu.usbmodemDC5475C56BE02", "label": "/dev/cu.usbmodemDC5475C56BE02", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x1002", "serialNumber": "DC5475C56BE0", "vid": "0x2341" }, "hardware_id": "DC5475C56BE0" } } ]
cat ~/Documents/Arduino/r4/r4.ino #include <HID.h> void setup() {} void loop() {}% 
 ./arduino-cli upload -b arduino:renesas_uno:unor4wifi -p /dev/cu.usbmodemDC5475C56BE02 ~/Documents/Arduino/r4 --format json
{ "stdout": "Erase flash\n\nDone in 0.001 seconds\nWrite 46784 bytes to flash (12 pages)\n\r[ ] 0% (0/12 pages)\r[== ] 8% (1/12 pages)\r[===== ] 16% (2/12 pages)\r[======= ] 25% (3/12 pages)\r[========== ] 33% (4/12 pages)\r[============ ] 41% (5/12 pages)\r[=============== ] 50% (6/12 pages)\r[================= ] 58% (7/12 pages)\r[==================== ] 66% (8/12 pages)\r[====================== ] 75% (9/12 pages)\r[========================= ] 83% (10/12 pages)\r[=========================== ] 91% (11/12 pages)\r[==============================] 100% (12/12 pages)\nDone in 2.968 seconds\n", "stderr": "", "updated_upload_port": { "address": "/dev/cu.usbmodemDC5475C56BE02", "label": "/dev/cu.usbmodemDC5475C56BE02", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x1002", "serialNumber": "DC5475C56BE0", "vid": "0x2341" }, "hardware_id": "DC5475C56BE0" } }

The updated_upload_port#address is still /dev/cu.usbmodemDC5475C56BE02 👆, but the board list shows different: /dev/cu.usbmodem14201 👇.

./arduino-cli board list --format json
[ { "port": { "address": "/dev/cu.BLTH", "label": "/dev/cu.BLTH", "protocol": "serial", "protocol_label": "Serial Port" } }, { "port": { "address": "/dev/cu.Bluetooth-Incoming-Port", "label": "/dev/cu.Bluetooth-Incoming-Port", "protocol": "serial", "protocol_label": "Serial Port" } }, { "matching_boards": [ { "name": "Arduino UNO R4 WiFi", "fqbn": "arduino:renesas_uno:unor4wifi" } ], "port": { "address": "/dev/cu.usbmodem14201", "label": "/dev/cu.usbmodem14201", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x006D", "serialNumber": "3507110A35323634DB5233324B572D0A", "vid": "0x2341" }, "hardware_id": "3507110A35323634DB5233324B572D0A" } } ]

CLI version: 990d93a

I don't know whether this PR should cover this use case. Can you please help, @per1234? Thank you!

@kittaakos
Copy link
Contributor

kittaakos commented Aug 10, 2023

I am using d1869b6, and after manually running ~20 consecutive uploads, I no longer see the bug. ✅

arduino-cli Version: git-snapshot Commit: d1869b66 Date: 2023-08-11T11:20:14Z 

This one is more likely a bug. I do not have the steps to reproduce it consistently, but I have noticed some odd behavior in IDE2, and it happens with pure CLI after the upload (repeat the upload a few times (~10), and it will break once).

I have a MKR 1000 attached on /dev/cu.usbmodem14101. I do an upload and the updated_upload_port#address is strange:

./arduino-cli upload -b arduino:samd:mkr1000 -p /dev/cu.usbmodem14101 ~/Documents/Arduino/monitor3 --format json
{ "stdout": "Atmel SMART device 0x10010005 found\nDevice : ATSAMD21G18A\nChip ID : 10010005\nVersion : v2.0 [Arduino:XYZ] Dec 20 2016 15:36:43\nAddress : 8192\nPages : 3968\nPage Size : 64 bytes\nTotal Size : 248KB\nPlanes : 1\nLock Regions : 16\nLocked : none\nSecurity : false\nBoot Flash : true\nBOD : true\nBOR : true\nArduino : FAST_CHIP_ERASE\nArduino : FAST_MULTI_PAGE_WRITE\nArduino : CAN_CHECKSUM_MEMORY_BUFFER\nErase flash\ndone in 0.700 seconds\n\nWrite 11344 bytes to flash (178 pages)\n\r[========== ] 35% (64/178 pages)\r[===================== ] 71% (128/178 pages)\r[==============================] 100% (178/178 pages)\ndone in 0.074 seconds\n\nVerify 11344 bytes of flash with checksum.\nVerify successful\ndone in 0.010 seconds\nCPU reset.\n", "stderr": "", "updated_upload_port": { "address": "/dev/tty.usbmodem14101", "label": "/dev/cu.usbmodem14101", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804E", "serialNumber": "94A3397C5150435437202020FF150838", "vid": "0x2341" }, "hardware_id": "94A3397C5150435437202020FF150838" } }

updated_upload_port#address is /dev/tty.usbmodem14101, but the label is /dev/cu.usbmodem14101. The board list does not see /dev/tty.

./arduino-cli board list --format json
[ { "port": { "address": "/dev/cu.BLTH", "label": "/dev/cu.BLTH", "protocol": "serial", "protocol_label": "Serial Port" } }, { "port": { "address": "/dev/cu.Bluetooth-Incoming-Port", "label": "/dev/cu.Bluetooth-Incoming-Port", "protocol": "serial", "protocol_label": "Serial Port" } }, { "matching_boards": [ { "name": "Arduino MKR1000", "fqbn": "arduino:samd:mkr1000" } ], "port": { "address": "/dev/cu.usbmodem14101", "label": "/dev/cu.usbmodem14101", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804E", "serialNumber": "94A3397C5150435437202020FF150838", "vid": "0x2341" }, "hardware_id": "94A3397C5150435437202020FF150838" } } ]

It's currently a blocking issue in IDE2. Thanks!

@per1234
Copy link
Contributor

per1234 commented Aug 11, 2023

I received specific steps to force the port change with Uno R4 WiFi. In this case, the updated_upload_port does not tell the new port, but the port has changed.

Hi @kittaakos. I apologize for the confusion. I intended to circle back to our Slack convo about this after I had investigated but hadn't found the time.

I provided these instructions purely as a response to the request for demonstrations of an upload causing a port change. I hadn't looked into how the system implemented in this PR would work at that time so I didn't realize that there were some unique considerations for this particular demonstration.

A port change takes some time for the operating system to de-enumerate the old port and then enumerate the new port. So Arduino CLI's system for detecting a port change requires it to wait some time while watching for the appearance of a new port, timing out and falling back to using the original port if the new port doesn't appear within the timeout duration.

Some boards (e.g., classic UNO, Mega, Nano Every) will never experience a port change on upload. Since it would be inefficient for Arduino CLI to wait around for a port change in this case it is possible for the platform author to configure the board definition to skip the wait step by setting the upload.wait_for_upload_port property to false in the board definition.

So for boards with this upload.wait_for_upload_port property set to false, Arduino CLI simply returns the original port in the updated_upload_port of the upload response.

ifuploadProperties.GetBoolean("upload.wait_for_upload_port") &&watch!=nil {
godetectUploadPort(uploadCtx, port, watch, updatedUploadPort)

There is an unusual situation with the UNO R4 WiFi where, where the average sketch will not produce a port change due to the board having a dedicated USB "bridge" module, but if the sketch uses USB HID capabilities (which, in a innovative approach, are provided by the "bridge" chip) then the port change does occur.

The board definition of the UNO R4 WiFi sets the upload.wait_for_upload_port property to false:

https://github.com/arduino/ArduinoCore-renesas/blob/1.0.2/boards.txt#L138

This is arguably incorrect, but setting it to true would cause the wait timeout delay to be added to the upload time whenever the user is not using a sketch with HID (which is probably 95% of the time). So the decision was made to accept a poor user experience when the HID capabilities are used:

arduino/ArduinoCore-renesas#74 (comment)

The mitigation measure for the existing poor user experience has been to document the special procedures that are required when using the board's HID capabilities (arduino/docs-content#1246, arduino/help-center-content#258). I'll have to add the requirement of manually selecting the post-upload port to that documentation once we ship arduino/arduino-ide#2165

It is possible to adjust the configuration of the UNO R4 WiFi board definition to simulate a port change on a board that is correctly configured for such a thing:

  1. Open the file at this path:
    ~/Library/Arduino15/packages/arduino/hardware/renesas_uno/1.0.2/boards.txt 
  2. Change line 138 from:
    unor4wifi.upload.wait_for_upload_port=false 
    to:
    unor4wifi.upload.wait_for_upload_port=true 
  3. Save the file.
  4. Restart Arduino IDE if it is running to trigger a reload of the updated platform configuration file.

Unfortunately I find that Arduino CLI is still unable to correctly identify the post-upload port. I'm not sure why. I see the port is seen, but has a different hardware_id field value:

time="2023-08-10T21:02:19-07:00" level=trace msg="New upload port candidate" event="event_type:\"add\" port:{matching_boards:{name:\"Arduino UNO R4 WiFi\" fqbn:\"arduino:renesas_uno:unor4wifi\"} port:{address:\"COM12\" label:\"COM12\" protocol:\"serial\" protocol_label:\"Serial Port (USB)\" properties:{key:\"pid\" value:\"0x006D\"} properties:{key:\"serialNumber\" value:\"331226165931383508BC33324B572E41\"} properties:{key:\"vid\" value:\"0x2341\"} hardware_id:\"331226165931383508BC33324B572E41\"}}" task=port_detection time="2023-08-10T21:02:19-07:00" level=trace msg="New candidate port did not match desired HW ID, keep watching..." task=port_detection time="2023-08-10T21:02:23-07:00" level=trace msg="Timeout waiting for candidate port" task=port_detection 
@per1234
Copy link
Contributor

updated_upload_port#address is /dev/tty.usbmodem14101, but the label is /dev/cu.usbmodem14101

I am also able to reproduce this. I see that the updated_upload_port.address field value is that of the port used during the upload instead of the post-upload port:

$ arduino-cli upload -b arduino:samd:mkrzero -p COM32 --format json --log-file logs.log --log-level trace { "stdout": "Atmel SMART device 0x10010005 found\r\nDevice : ATSAMD21G18A\r\nChip ID : 10010005\r\nVersion : v2.0 [Arduino:XYZ] Apr 11 2019 13:09:53\r\nAddress : 8192\r\nPages : 3968\r\nPage Size : 64 bytes\r\nTotal Size : 248KB\r\nPlanes : 1\r\nLock Regions : 16\r\nLocked : none\r\nSecurity : false\r\nBoot Flash : true\r\nBOD : false\r\nBOR : false\r\nArduino : FAST_CHIP_ERASE\r\nArduino : FAST_MULTI_PAGE_WRITE\r\nArduino : CAN_CHECKSUM_MEMORY_BUFFER\r\nErase flash\r\ndone in 0.915 seconds\r\n\r\nWrite 11068 bytes to flash (173 pages)\r\n\r[=========== ] 36% (64/173 pages)\r[====================== ] 73% (128/173 pages)\r[==============================] 100% (173/173 pages)\r\ndone in 0.083 seconds\r\n\r\nVerify 11068 bytes of flash with checksum.\r\nVerify successful\r\ndone in 0.010 seconds\r\nCPU reset.\r\n", "stderr": "", "updated_upload_port": { "address": "COM33", "label": "COM32", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804F", "serialNumber": "F0F2036051504C3750202020FF0F2520", "vid": "0x2341" }, "hardware_id": "F0F2036051504C3750202020FF0F2520" } } $ arduino-cli board list --format json [ { "matching_boards": [ { "name": "Arduino MKRZERO", "fqbn": "arduino:samd:mkrzero" } ], "port": { "address": "COM32", "label": "COM32", "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804F", "serialNumber": "F0F2036051504C3750202020FF0F2520", "vid": "0x2341" }, "hardware_id": "F0F2036051504C3750202020FF0F2520" } }, 
time="2023-08-10T21:46:08-07:00" level=trace msg="New upload port candidate" event="event_type:\"add\" port:{matching_boards:{name:\"Arduino MKRZERO\" fqbn:\"arduino:samd:mkrzero\"} port:{address:\"COM32\" label:\"COM32\" protocol:\"serial\" protocol_label:\"Serial Port (USB)\" properties:{key:\"pid\" value:\"0x804F\"} properties:{key:\"serialNumber\" value:\"F0F2036051504C3750202020FF0F2520\"} properties:{key:\"vid\" value:\"0x2341\"} hardware_id:\"F0F2036051504C3750202020FF0F2520\"}}" task=port_detection time="2023-08-10T21:46:08-07:00" level=trace msg="Found new upload port!" task=port_detection 
Previously only the pointer was copied, thus making changes in `actualPort` to be reflected also to `port`. This lead to some weird result in the `updatedUploadPort` result: { "stdout": "Verify 11344 bytes of flash with checksum.\nVerify successful\ndone in 0.010 seconds\nCPU reset.\n", "stderr": "", "updated_upload_port": { "address": "/dev/tty.usbmodem14101", <------- this address... "label": "/dev/cu.usbmodem14101", <------- ...is different from the label "protocol": "serial", "protocol_label": "Serial Port (USB)", "properties": { "pid": "0x804E", "serialNumber": "94A3397C5150435437202020FF150838", "vid": "0x2341" }, "hardware_id": "94A3397C5150435437202020FF150838" } }
@cmaglie
Copy link
MemberAuthor

updated_upload_port#address is /dev/tty.usbmodem14101, but the label is /dev/cu.usbmodem14101. The board list does not see /dev/tty.

I've pushed two commits that should fix this problem.

@cmagliecmaglieforce-pushed the board_port_after_upload branch from 1d3e528 to af873e0CompareAugust 11, 2023 11:40
We must acesss the gRPC API only until we cross the `command` package border. Once we are inside the `command` package we should use the internal API only.
Now the upload detects cases when the upload port is "unstable", i.e. the port changes even if it shouldn't (because the wait_for_upload_port property in boards.txt is set to false). This change should make the upload process more resilient.
@cmaglie
Copy link
MemberAuthor

I've implemented a workaround for the weird port change on the Uno R4, so the detection now work more or less like this:

  • if wait_for_upload_port is true: after the upload wait for a new port to become available:

    • if the port has the same hardware ID as the original upload port, then use it immediately
    • otherwise keep the port but wait up to 5 seconds in case another port that has a matching hardware ID shows up
  • if wait_for_upload_port is false: after the upload wait 1 second and check if the upload port is still alive:

    • if it's still alive, use the old upload port
    • otherwise consider the port lost, and wait for an upload port using the same procedure as for wait_for_upload_port == true
Copy link
Contributor

@per1234per1234 left a comment

Choose a reason for hiding this comment

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

UPDATE: Fixed by e12a068

Panic when upload causes disappearance of port

On boards with native USB, the USB stack that produces the USB CDC serial port used for uploading is running on the same microcontroller as the sketch program. This means that, when working with these board, it is common for sketch uploads to cause the disappearance of the port, either unexpectedly due to a bug such as a divide by zero or expectedly such as when putting the MCU to sleep.

🐛 If a sketch upload causes the disappearance of the port, Arduino CLI panics.

To reproduce

Equipment

Steps

Upload a sketch that causes the loss of the port of your board:

$ ./arduino-cli version arduino-cli.exe Version: git-snapshot Commit: e9e5fbdd Date: 2023-08-16T03:41:25Z $ mkdir "/tmp/NoInterruptsSketch" $ printf "void setup() {noInterrupts();}\nvoid loop() {}\n" > "/tmp/NoInterruptsSketch/NoInterruptsSketch.ino" $ ./arduino-cli compile --fqbn arduino:avr:leonardo [...] $ ./arduino-cli upload --fqbn arduino:avr:leonardo --port COM23 --verbose "/tmp/NoInterruptsSketch" Performing 1200-bps touch reset on serial port COM23 Waiting for upload port... Upload port found on COM24 "C:\Users\per\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude" "-CC:\Users\per\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -V -patmega32u4 -cavr109 "-PCOM24" -b57600 -D "-Uflash:w:C:\Users\per\AppData\Local\Temp\arduino\sketches\892D5E2A267501D13FBB3246DF62F6BE/NoInterruptsSketch.ino.hex:i" avrdude: Version 6.3-20190619 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "C:\Users\per\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" Using Port : COM24 Using Programmer : avr109 Overriding Baud Rate : 57600 AVR Part : ATmega32U4 Chip Erase delay : 9000 us PAGEL : PD7 BS2 : PA0 RESET disposition : dedicated RETRY pulse : SCK serial program mode : yes parallel program mode : yes Timeout : 200 StabDelay : 100 CmdexeDelay : 25 SyncLoops : 32 ByteDelay : 0 PollIndex : 3 PollValue : 0x53 Memory Detail : Block Poll Page Polled Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- eeprom 65 20 4 0 no 1024 4 0 9000 9000 0x00 0x00 flash 65 6 128 0 yes 32768 128 256 4500 4500 0x00 0x00 lfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00 hfuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00 efuse 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00 lock 0 0 0 0 no 1 0 0 9000 9000 0x00 0x00 calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 Programmer Type : butterfly Description : Atmel AppNote AVR109 Boot Loader Connecting to programmer: . Found programmer: Id = "CATERIN"; type = S Software Version = 1.0; No Hardware Version given. Programmer supports auto addr increment. Programmer supports buffered memory access with buffersize=128 bytes. Programmer supports the following devices: Device code: 0x44 avrdude: devcode selected: 0x44 avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e9587 (probably m32u4) avrdude: reading input file "C:\Users\per\AppData\Local\Temp\arduino\sketches\892D5E2A267501D13FBB3246DF62F6BE/NoInterruptsSketch.ino.hex" avrdude: writing flash (3464 bytes): Writing | ################################################## | 100% 0.27s avrdude: 3464 bytes of flash written avrdude done. Thank you. panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x50 pc=0xe60dea] goroutine 1 [running]: github.com/arduino/arduino-cli/arduino/discovery.(*Port).ToRPC(...) E:/git/arduino-cli/arduino/discovery/discovery.go:108 github.com/arduino/arduino-cli/commands/upload.runProgramAction(0xc000c6a700, 0x65d6de?, {0x0, 0x0}, {0x0, 0x0}, {0xc00011a060, 0x14}, 0x1a321a0?, {0x0, ...}, ...) E:/git/arduino-cli/commands/upload/upload.go:515 +0x54ca github.com/arduino/arduino-cli/commands/upload.Upload({0x19d9060?, 0x0?}, 0xc00010c000, {0x13a2a60, 0xc0001151a0}, {0x13a2a60, 0xc0001151b8}) E:/git/arduino-cli/commands/upload/upload.go:146 +0x468 github.com/arduino/arduino-cli/internal/cli/upload.runUploadCommand(0xc00038c280?, {0xc000170cc0, 0x1, 0x6?}) E:/git/arduino-cli/internal/cli/upload/upload.go:171 +0xbba github.com/spf13/cobra.(*Command).execute(0xc00038c280, {0xc000170c60, 0x6, 0x6}) C:/Users/per/go/pkg/mod/github.com/spf13/cobra@v1.2.1/command.go:860 +0x663 github.com/spf13/cobra.(*Command).ExecuteC(0xc0002c0500) C:/Users/per/go/pkg/mod/github.com/spf13/cobra@v1.2.1/command.go:974 +0x3bd github.com/spf13/cobra.(*Command).Execute(0x0?) C:/Users/per/go/pkg/mod/github.com/spf13/cobra@v1.2.1/command.go:902 +0x19 main.main() E:/git/arduino-cli/main.go:31 +0xea 

🐛 The process panicked.

Expected behavior

Arduino CLI handles the disappearance of the board's port after an upload gracefully.

Arduino CLI version

e9e5fbd

Operating system

Windows 11

Additional context

I bisected the regression to e9e5fbd (does not occur at the previous commit f8394fb).

Recovering the board from the no port state

If you performed the demonstration I provided above, the board will be in a state where it can't be uploaded to as usual. It can be recovered by performing the following procedure:

  1. Prepare a sketch that doesn't contain any problematic code:
    $ arduino-cli sketch new "/tmp/BareMinimum" 
  2. Invoke an arduino-cli upload command for the sketch without a --port flag.
    For example:
    $ arduino-cli upload --fqbn arduino:avr:leonardo --verbose "/tmp/BareMinimum" 
  3. Watch the command output until you see the following line:
    Waiting for upload port... 
  4. Press and release the reset button on the board twice quickly to activate the bootloader.

The upload should now finish successfully. After that the board will produce a port once more and you can go back to uploading via the standard procedure.

per1234
per1234 previously requested changes Aug 16, 2023
Copy link
Contributor

@per1234per1234 left a comment

Choose a reason for hiding this comment

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

UPDATE: Fixed by 862bbe2

updated_upload_port may be different than upload target port when uploading to boards that produce multiple ports

Some Arduino boards produce multiple ports. The algorithm for identifying updated_upload_port selects the first of these to appear after an upload.

🐛 This port may be different from the one the user specified via the upload request, which will result in the updated_upload_port value being different than expected.

To reproduce

Equipment

  • Board that produces multiple ports, such as:
    • Nano ESP32
    • Teensy board (when running a sketch that was compiled with the usb custom board option set to one of the "serial" options)

Demo

$ ./arduino-cli version arduino-cli.exe Version: git-snapshot Commit: e9e5fbdd Date: 2023-08-16T03:41:25Z $ ./arduino-cli sketch new /tmp/BareMinimum [...] $ ./arduino-cli board list Port Protocol Type Board Name FQBN Core 1-7.1.4.1 dfu USB DFU Arduino Nano ESP32 arduino:esp32:nano_nora arduino:esp32 COM11 serial Serial Port (USB) Arduino Nano ESP32 arduino:esp32:nano_nora arduino:esp32 $ ./arduino-cli upload --fqbn arduino:esp32:nano_nora --protocol dfu --port 1-7.1.4.1 PERTILLISCH/sketches/BareMinimum/ dfu-util 0.11-arduino4 Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc. Copyright 2010-2021 Tormod Volden and Stefan Schmidt This program is Free Software and has ABSOLUTELY NO WARRANTY Please report bugs to http://sourceforge.net/p/dfu-util/tickets/ Opening DFU capable USB device... Device ID 2341:0070 Device DFU version 0101 Claiming USB DFU Interface... Setting Alternate Interface #0 ... Determining device status... DFU state(2) = dfuIDLE, status(0) = No error condition is present DFU mode device DFU version 0101 Device returned transfer size 4096 Copying data from PC to DFU device Download [=========================] 100% 286064 bytes Download done. DFU state(7) = dfuMANIFEST, status(0) = No error condition is present DFU state(2) = dfuIDLE, status(0) = No error condition is present Done! New upload port: COM11 (serial) $ ./arduino-cli board list Port Protocol Type Board Name FQBN Core 1-7.1.4.1 dfu USB DFU Arduino Nano ESP32 arduino:esp32:nano_nora arduino:esp32 COM11 serial Serial Port (USB) Arduino Nano ESP32 arduino:esp32:nano_nora arduino:esp32 

🐛 updated_upload_port was COM11 even though 1-7.1.4.1 was specified via the --port flag.

Expected behavior

updated_upload_port is the same (or equivalent) to the target port when such a port is available after upload.

Any additional ports produced by the board are only selected for updated_upload_port if there is no closer port match.

Arduino CLI version

e9e5fbd

Operating system

  • Windows
  • Linux
  • macOS

Operating system version

  • Windows 11
  • Ubuntu 22.04
  • macOS Ventura

Additional context

Admittedly the demonstration I provided of a 1-7.1.4.1 -> COM11 port change when uploading to Nano ESP32 is not very compelling. The user of this specific board would typically be better off with the serial protocol port selected when available. However, unexpected port selection changes in Arduino IDE when using Nano ESP32 was one of the faults that were specifically targeted for resolution by this work. I don't know how Arduino CLI behaves in this situation, but the direction of the port change fault as produced by the Arduino IDE port selection system was nondeterministic, meaning that some users experienced an unexpected change from the serial to the dfu protocol port, which caused Serial Monitor to no longer function (since there is no pluggable monitor for dfu protocol).

Even if the direction of the change is deterministically to the serial protocol port, for other boards this might be harmful. For example, the teensy protocol port is preferred over the serial port when using a Teensy board and Arduino CLI returns the serial port after uploading to the teensy port.

cmaglieand others added 2 commits August 16, 2023 10:12
Co-authored-by: per1234 <accounts@perglass.com>
@cmaglie
Copy link
MemberAuthor

Panic when upload causes disappearance of port
On boards with native USB, the USB stack that produces the USB CDC serial port used for uploading is running on the same microcontroller as the sketch program. This means that, when working with these board, it is common for sketch uploads to cause the disappearance of the port, either unexpectedly due to a bug such as a divide by zero or expectedly such as when putting the MCU to sleep.

Great catch, I've just pushed a commit to fix this!

The new algorithm takes into account the case where a single board may expose multiple ports, in this case the selection will increase priority to ports that: 1. have the same HW id as the user specified port for upload 2. have the same protocol as the user specified port for upload 3. have the same address as the user specified port for upload
@cmaglie
Copy link
MemberAuthor

cmaglie commented Aug 16, 2023

updated_upload_port may be different than upload target port when uploading to boards that produce multiple ports

updated_upload_port may be different than upload target port when uploading to boards that produce multiple ports
Some Arduino boards produce multiple ports. The algorithm for identifying updated_upload_port selects the first of these to appear after an upload.

🐛 This port may be different from the one the user specified via the upload request, which will result in the updated_upload_port value being different than expected.

Ok, I've pushed also a patch for this issue, the detection algorithm now prioritizes ports having (in order of importance):

  1. the same HW ID of the upload port
  2. the same protocol of the upload port
  3. the same address as the upload port

BTW to allow this selection I needed to introduce a delay to allow the discoveries to detect the set of available ports and report them to the watcher. The initial delay is 5 seconds and it may be cut to 1 sec if a port with matching HW ID is found. From my preliminary tests, it seems to work fine but I'm not 100% sure about this last point (reducing the timeout to 1 sec), so more testing is very welcome.

About testing, I'd like to have feedback also:

  • on port detection delays, since it may take up to 5 seconds AFTER the upload, I'd like to understand if this is noticeable
  • in case the port selection is not correct, please use the --log --log-level trace flag and paste the logs after the line:
TRAC[0005] Detecting new board port after upload task=port_detection 

For example here is the trace I'm interested in:

[...] TRAC[0005] Detecting new board port after upload task=port_detection TRAC[0005] Ignored watcher event before upload event="&{add /dev/ttyACM1 builtin:serial-discovery}" task=port_detection TRAC[0005] Ignored watcher event before upload event="&{add /dev/ttyS0 builtin:serial-discovery}" task=port_detection DEBU[0005] LAST: map[/dev/ttyACM1:true /dev/ttyS0:true] phase="board reset" DEBU[0005] TOUCH: /dev/ttyACM1 phase="board reset" INFO[0005] Performing 1200-bps touch reset on serial port /dev/ttyACM1 phase="board reset" TRAC[0006] Executing upload tool: "/home/megabug/.arduino15/packages/arduino/tools/bossac/1.9.1-arduino5/bossac" --port=ttyACM1 -U -e -w "/tmp/arduino/sketches/002050EAA7EFB9A4FC451CDFBC0FA2D3/Blink.ino.bin" -R phase=upload Erase flash Done in 0.001 seconds Write 46744 bytes to flash (12 pages) [==============================] 100% (12/12 pages) Done in 2.966 seconds TRAC[0009] Upload successful INFO[0010] from discovery builtin:serial-discovery received message type: remove, porta: /dev/ttyACM1 TRAC[0010] Candidate port is no longer available event="&{remove /dev/ttyACM1 builtin:serial-discovery}" task=port_detection TRAC[0010] User-specified port has been disconnected, now waiting for upload port, timeout extended by 5 seconds task=port_detection INFO[0010] from discovery builtin:serial-discovery received message type: add, porta: /dev/ttyACM0 TRAC[0010] Found new upload port candidate (prio=100) event="&{add /dev/ttyACM0 builtin:serial-discovery}" task=port_detection TRAC[0010] New candidate port match the desired HW ID, timeout reduced to 1 second. task=port_detection TRAC[0011] Timeout waiting for candidate port selected_port=/dev/ttyACM0 task=port_detection 
@per1234per1234 dismissed their stale reviewAugust 17, 2023 01:16

Previous reviews have been resolved. Thanks!

@cmagliecmaglie merged commit 38479dc into arduino:masterAug 18, 2023
@cmagliecmaglie deleted the board_port_after_upload branch August 18, 2023 12:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment
Labels
criticality: highestOf highest impactpriority: highResolution is a high prioritytopic: codeRelated to content of the project itselftopic: gRPCRelated to the gRPC interfacetype: enhancementProposed improvement
4 participants
@cmaglie@kittaakos@per1234@alessio-perugini
close