sudar / Arduino-Makefile

Makefile for Arduino sketches. It defines the workflows for compiling code, flashing it to Arduino and even communicating through Serial.
http://hardwarefun.com/tutorials/compiling-arduino-sketches-using-makefile
GNU Lesser General Public License v2.1
2.02k stars 448 forks source link

Reset of Leonardo boards #30

Closed joh closed 11 years ago

joh commented 11 years ago

Reset of Leonardo type boards is done a bit differently:

Rather than requiring a physical press of the reset button before an upload, the Leonardo is designed in a way that allows it to be reset by software running on a connected computer. The reset is triggered when the Leonardo's virtual (CDC) serial / COM port is opened at 1200 baud and then closed. When this happens, the processor will reset, breaking the USB connection to the computer (meaning that the virtual serial / COM port will disappear). After the processor resets, the bootloader starts, remaining active for about 8 seconds.

Please consider adding support for this to ard-reset-arduino. I'm no Perl wiz, but the following Python code did the trick:

import serial

ser = serial.Serial("/dev/ttyACM0", 1200)
ser.open()
ser.close()

Cheers!

guicho271828 commented 11 years ago

I encountered the same problem.

guicho271828 commented 11 years ago

I fixed the problem with your code joh, along with minor fixes. see #349d254

guicho271828 commented 11 years ago

sorry, it was #37

sudar commented 11 years ago

I have created a separate branch by taking @guicho271828 code. https://github.com/sudar/Arduino-Makefile/tree/leonardo

Kindly check it and let me know if it works properly. Will merge it with master once it is confirmed to work.

maleadt commented 11 years ago

Shouldn't this also be the case for the Arduino Micro (BOARD_TAG = micro)?

Rather than requiring a physical press of the reset button before an upload, the Micro is designed in a way that allows it to be reset by software running on a connected computer. The reset is triggered when the Micro's virtual (CDC) serial / COM port is opened at 1200 baud and then closed. When this happens, the processor will reset, breaking the USB connection to the computer (meaning that the virtual serial / COM port will disappear). After the processor resets, the bootloader starts, remaining active for about 8 seconds. The bootloader can also be initiated by pressing the reset button on the Micro. Note that when the board first powers up, it will jump straight to the user sketch, if present, rather than initiating the bootloader.

I just tested the new ard-reset-leonardo, and it also works for an Arduino Micro.

sej7278 commented 11 years ago

there is no ard-reset-leonardo now, we merged the functionality into the makefile as part of commit 60ca7d285285

could you try ard-reset-arduino --leonardo /dev/ttyUSB0 (needs 0.10-6 makefile or later) and if that works we can add the micro tag to the leonardo routine

sudar commented 11 years ago

Shouldn't this also be the case for the Arduino Micro (BOARD_TAG = micro)?

The only reason why this is not added is because I don't have a micro to test it with :)

I just tested the new ard-reset-leonardo, and it also works for an Arduino Micro.

We have rewrote the way reset is handled for leonardo and the bin/ard-reset-leonardo script is merged inside bin/ard-reset-arduino itself in commit 60ca7d285285a8dda3beb8074dc8fd7427f850a4 . Also check out #62

Can you kindly test whether this works for micro as well. If yes, then I can do the required code changes and push an update.

I have also created #80 to track this.

sudar commented 11 years ago

@sej7278 You beat me by a couple of minutes ;)

sej7278 commented 11 years ago

mwahaha, you can't beat me even with your time machine!

i'll have a pull request up in a mo as soon as i can think of a way to check for leonardo OR micro in an ifeq()

maleadt commented 11 years ago

Strangely, the new ard-reset-arduino script does not work properly on my Micro, at least not in all circumstances. More specifically, when something has been using the serial port (boblight in my case), ard-reset-ardiuno doesn't manage to reboot the Micro, while the Python script which was used in ard-reset-leonardo does work (even without the redundant open & close calls it originally contained):

#! /usr/bin/python

import sys
import serial

ser = serial.Serial(sys.argv[1], 57600)
ser.setBaudrate(1200)
ser.close()

If the Micro had been idle, ard-reset-arduino succeeds in rebooting the device. Strange.

sej7278 commented 11 years ago

can you try modifying the old ard-reset-leonardo python script so that it starts at 1200 baud instead of 57600, as there was something about having to change from the initial baud rather than just starting at 1200bps in some comment somewhere. so i guess:

#!/usr/bin/python

import sys
import serial

ser = serial.Serial(sys.argv[1], 1200)
ser.setBaudrate(1200)
ser.close()

if that fails to reset, then we can modify ard-reset-arduino --leonardo to start at 57600 and change to 1200

maleadt commented 11 years ago

Nope, doesn't matter. So, this works:

#! /usr/bin/python

import sys
import serial

ser = serial.Serial(sys.argv[1], 1200)
ser.close()

... while this doesn't (always):

#!/usr/bin/perl

use strict;
use warnings;

use Device::SerialPort;

my $portName = shift || die();
my $port = new Device::SerialPort($portName, 0) or die();
$port->baudrate(1200) or die();
$port->write_settings() or die();
$port->close() or die();
sej7278 commented 11 years ago

can you try your perl without the dies as you have to open, set baud, close; and if one of those dies it won't reset. but i've not idea why otherwise.

maleadt commented 11 years ago

I added the die() statements in case one of the commands would fail; but none of them fail, so removing the die() statements doesn't change anything...

sej7278 commented 11 years ago

well if none of the commands fail, that's very odd. points to an OS issue perhaps, what platform is this - don't say fscking OSX! ;)

maleadt commented 11 years ago

Heh no, it's Linux 3.8, running under Ubuntu 13.04. I'd rather think it to be a Device::SerialPort bug, but I don't have time to look at it myself...

mjoldfield commented 11 years ago

If you want to debug this further, it might be worth logging the system calls made by the python and Perl programs. On linux strace should do this.

sej7278 commented 11 years ago

@mjoldfield strace is a good idea thanks.

@maleadt I did have a bit of a grok around pySerial to compare with Device::SerialPort but its too much work for the moment. strace might show something obvious hopefully - probably something not freed or blocking the filehandle, or maybe something python does silently.

i'm not even sure if wait-connection-leonardo is required these days.

sudar commented 11 years ago

I tried to reset leonardo by using both perl and python in Ubuntu.

Both the scripts reset the board properly for me.

I have also run strace and have attached the output of both perl and python as well as the code I used in this gist

https://gist.github.com/sudar/5823250

One observation I had right away is that the trace of python has more than double the number of lines when compared with the perl version

sej7278 commented 11 years ago

they're both opening in the same way, perl seems to be going over-the-top changing baud rates and then failing to close the filehandle:

20:03:03.849542 open("/dev/ttyACM0", O_RDWR|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 3 <0.001769>
20:03:03.851439 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000016>
20:03:03.851527 _llseek(3, 0, 0xbfb8cf30, SEEK_CUR) = -1 ESPIPE (Illegal seek) <0.000020>
20:03:03.851589 fstat64(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(166, 0), ...}) = 0 <0.000009>
20:03:03.851663 fcntl64(3, F_SETFD, FD_CLOEXEC) = 0 <0.000008>
20:03:03.851712 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:03:03.851838 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:03:03.851890 ioctl(3, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0 <0.000010>
20:03:03.851944 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 <0.000007>
20:03:03.852053 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:03:03.852106 ioctl(3, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000191>
20:03:03.852347 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:03:03.852437 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:03:03.852490 ioctl(3, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000010>
20:03:03.852543 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:03:03.852606 ioctl(3, TCFLSH, 0x2) = 0 <0.000010>
20:03:03.852676 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:03:03.852728 ioctl(3, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:03:03.852781 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:03:03.852835 close(3) = 0 <0.017473>
20:03:03.870372 close(3) = -1 EBADF (Bad file descriptor) <0.000014>

python just does:

20:02:51.924738 open("/dev/ttyACM0", O_RDWR|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 3 <0.001577>
20:02:51.926379 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000015>
20:02:51.926504 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:02:51.926559 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000009>
20:02:51.926612 ioctl(3, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000679>
20:02:51.927346 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0 <0.000008>
20:02:51.927409 close(3) = 0 <0.017918>
sej7278 commented 11 years ago

I've added a pull request just to add the Micro to the same reset routine as the Leonardo.

We can revisit why its not working all the time when we have some time to debug, but with my Boarduino I cannot get it to fail once.

sudar commented 11 years ago

I've added a pull request just to add the Micro to the same reset routine as the Leonardo.

Thanks. I just merged it.

matthijskooijman commented 11 years ago

It seems there is still an issue with the Leonardo, mine will not properly reset on my Linux machine:

/home/matthijs/docs/Electronics/Arduino/Arduino-Makefile/bin/ard-reset-arduino --caterina  /dev/ttyACM0
(...)
Connecting to programmer: .
Found programmer: Id = "TESTATA"; type = A

Manually running ard-reset-arduino and then checking dmesg also shows the device is not properly resetting.

matthijskooijman commented 11 years ago

My strace log (made with -e trace=file,ioctl,close) is a bit different, though:

stat("/dev/ttyACM0", {st_mode=S_IFCHR|S_ISVTX|0660, st_rdev=makedev(166, 0), ...}) = 0
open("/dev/ttyACM0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 4
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, TCFLSH, 0x2)                   = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
close(4)                                = 0
close(4)                                = -1 EBADF (Bad file descriptor)
Forcing reset using 1200bps open/close on port /dev/ttyACM0
stat("/dev/ttyACM0", {st_mode=S_IFCHR|S_ISVTX|0660, st_rdev=makedev(166, 0), ...}) = 0
/dev/ttyACM0 has come back after reset
close(3)   

The close works, but it actually resets the baudrate to 57600 before closing it seems :-S

matthijskooijman commented 11 years ago

Oh wait, the close didn't work, I was looking at the wrong close :-)

matthijskooijman commented 11 years ago

Also, it seems that just before the close, the perl library resets the baudrate to whatever it was at the start. This doesn't happen in the traces posted by sej7278, but I suspect this is because his python script left the baudrate at 1200 and doesn't try to change it back.

Also, it seems the descriptor is closed twice: The first time it works, the second time it (obviously) fails.

matthijskooijman commented 11 years ago

Just running stty -F /dev/ttyACM0 1200 works to reset my Leonardo and afterwards the ard-reset-arduino script also works. I suspect that this means that the "restore baudrate before close" thingy actually messes up the reset and that the reset is triggered by the baudrate set at port close time.

sudar commented 11 years ago

Yeah even I was facing this issue when I try to reset leonardo using Perl.

If I use the Python script, then it resets all the time. But if I use Perl script, then it resets, only if it was previously reset using Python script or by using stty or through Arduino IDE.

matthijskooijman commented 11 years ago

Heh, apologies for the spam run, but I think I've found a solution. Not sure if I like it, though, but at least it confirms my previous suspicions.

I just added an explicit file descriptor close in the script, which circumvents the "restore baudrate" feature. The relevant code now looks like:

    $p->baudrate(1200);
    $p->write_settings;
    POSIX::close($p->{FD});
    $p->close;

Some things I wonder:

The strace now looks like:

stat("/dev/ttyACM0", {st_mode=S_IFCHR|S_ISVTX|0660, st_rdev=makedev(166, 0), ...}) = 0
open("/dev/ttyACM0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 4
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B57600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_START or TCSETS, {B1200 -opost -isig -icanon -echo ...}) = 0
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B1200 -opost -isig -icanon -echo ...}) = 0
close(4)                                = 0
ioctl(4, TCFLSH, 0x2)                   = -1 EBADF (Bad file descriptor)
ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff9f375050) = -1 EBADF (Bad file descriptor)
ioctl(4, SNDCTL_TMR_START or TCSETS, {B57600 -opost -isig -icanon -echo ...}) = -1 EBADF (Bad file descriptor)
close(4)                                = -1 EBADF (Bad file descriptor)
close(4)                                = -1 EBADF (Bad file descriptor)
Forcing reset using 1200bps open/close on port /dev/ttyACM0
stat("/dev/ttyACM0", {st_mode=S_IFCHR|S_ISVTX|0660, st_rdev=makedev(166, 0), ...}) = 0
/dev/ttyACM0 has come back after reset
sudar commented 11 years ago

Wouldn't it make sense to actually restore the baudrate after the device has reset?

The Arduino IDE does this. They explicitly restore the bautrate to 57600 after reset.

matthijskooijman commented 11 years ago

Perhaps it makes sense to drop Device::SerialPort and just do a few calls to POSIX:: directly?

matthijskooijman commented 11 years ago

Or convert to Python altogether :-)

sudar commented 11 years ago

Or convert to Python altogether :-)

I am leaning towards this, instead of POSIX:: calls. Because we can support windows users as well if we choose Python.

sej7278 commented 11 years ago

When you say Windows users, how does any of this actually work on Windows - does it use cygwin or something? I think using stty from the Makefile directly is probably preferable to a perl/python script but doubt its very portable.

maybe we should drop windows support (nobody volunteered to test it even works) as they can use the CLI support in IDE 1.5 anyway, are windows users really going to install cygwin/gcc, perl/python and libraries to use this? at best we should limit ourselves to only using what comes bundled with the IDE on windows, which i assume is avr-gcc and avrdude and not much else.

the Device::SerialPort docs seem a bit unreliable as the call to destroy() doesn't even work as its already closed using close()

sudar commented 11 years ago

Technically if cygwin is installed, then most of what we have should work in Windows. But as you said, no one has tested it so far, so I don't know for sure.

The reason, why I said I would prefer Python over using POSIX:: calls is that, Python and PySerial are supported in windows and if someone becomes interested in using this in Windows at some point in future, the amount of effort required to add full support for Windows will be less.

sej7278 commented 10 years ago

I've added the regular arduino (DTR) functionality from ard-reset-arduino into the old python ard-reset-leonardo and tweaked it a bit. it now seems to work on my pro micro and mega2560.

could people please test it on a few platforms/boards please? its on gist

RichardBronosky commented 9 years ago

@matthijskooijman, @sudar, do you find that reseting a Leonardo or [ Sparkfun Pro] Micro requires 8 seconds? Is there a way around that?

Thanks for the stty suggestion. I have created https://gist.github.com/RichardBronosky/0500556194a45f2d4855#file-leoreset-sh that I use so I can reset with the device tethered from inside my backpack.

alejolp commented 8 years ago

Arduino Leonardo bootloader (Atmega32u4) requires DTR to 0 to fully reset into the bootloader. This is what the Arduino IDE is doing to reset a Leonardo:

    SerialPort serialPort = new SerialPort(iname);
    try {
      serialPort.openPort();
      serialPort.setParams(1200, 8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
      serialPort.setDTR(false);
      serialPort.closePort();
      return true;
    }

My Python script to reset a Leonardo is like this:

import serial
import sys
ser = serial.Serial()
ser.port = sys.argv[1]
ser.baudrate = 1200
ser.open()
ser.setDTR(0)
ser.close()

ALSO: sleep for 1 second before calling the programmer (avrdude, etc); give time to the bootloader to start.

CMCDragonkai commented 7 years ago

If I have a Python script running it at 9600 baud. Should the reset script be running at the closing of the python script, or the startup of the python script? Since my serial port is already open, do I need to close it, then open it, set it to 1200 baud then close it? Or can I just set it to 1200 then close it?

I just tried all the python varieties of resetting the leostick. None worked.

alejolp commented 7 years ago

@CMCDragonkai How are you calling the Python script? Are you sleeping for 1 second after closing the serial port and before trying to talk to the bootloader?

CMCDragonkai commented 7 years ago

@alejolp I don't quite understand your question. Are you saying to close the serial port, sleep for 1 second, then reopen? How does this reset the Leonardo (Leostick)? My serial ports are already being closed when I shutdown the Python program.

This answer says http://stackoverflow.com/a/21082531/582917 that you just need to toggle the DTR. What's the relationship between opening and closing the serial port, the DTR toggle and the 1200 baud? Why is all this required to just reset the board? In my case, the port is already open. Do I need to close, reopen at 9600, close, then open at 1200, then close? This one doesn't have a DTR toggle at all: https://defendtheplanet.net/2014/07/11/reset-arduino-micro-via-python-serial/

Here's my attempt:

def reset(controller):
    old_baud_rate = controller.baudrate
    if controller.is_open:
        controller.close()
    # set to serial 9600 and open and close
    controller.baudrate = 9600
    controller.open()
    controller.close()
    # set to serial 1200 and open and close (and set DTR)
    controller.baudrate = 1200
    controller.open()
    controller.setDTR(False)
    controller.close()
    timer.sleep(1)
    # reopen with old baud rate
    controller.baudrate = old_baud_rate
    controller.open()
alejolp commented 7 years ago

@CMCDragonkai The reset procedure is: open port at 1200 bps with DTR=false, and close. You need to wait 1 second to reset and the bootloader to kick in. Then you can talk to the bootloader and upload the firmware or whatever. If you have the serial port already open somewhere else, you need to close it first, otherwise you don't. Check out the context on Arduino IDE touching the port for reset.