espusb icon indicating copy to clipboard operation
espusb copied to clipboard

Integrate with esp8266/Arduino

Open probonopd opened this issue 9 years ago • 73 comments

Great project - I hope it can be nicely integrated with https://github.com/esp8266/Arduino (e.g., to do the uploading and "serial" monitor using just the ESP and no serial adapter).

I also cross-posted this at https://github.com/esp8266/Arduino/issues/2375

probonopd avatar Aug 08 '16 07:08 probonopd

This should be able to fit fairly well, but there's a lot I don't know. Only thing I'm kind worried about is the way my reflasher works is it uses a scratchpad, verifies, and then flashes itself. If something goes wrong or you break the USB interrupt, you would have to go back to serial flashing to get the thing recovered.

Another big recommendation is I know people would probably want to turn this into a CDC device, but I really believe using control messages is the best way to stage data back and forth. It sidesteps a lot of problems with endpoints and keeps things really organized.

P.S. Right now my focus is on two other aspects of the project. (1) Demoizing the project and (2) Trying to get USB 1.1 Full Speed working. I still don't know if #2 is possible, but I am going to keep my focus there.

I also can continuously work to shrink my code size.

Who all knows much about how to set up bootloaders, etc?

cnlohr avatar Aug 08 '16 15:08 cnlohr

Just realized igrr (https://github.com/igrr) the main contributor of esp8266/Arduino is sitting at @Espressif. He probably can help you with your Full Speed ambitions (interrupt/dma issues and what not)

bitluni avatar Aug 09 '16 12:08 bitluni

I just made a post on their forums (though it looks like posts have to be approved) How would I go about requesting this from him? Should I be requesting it of him directly? I know RichardS said he's already on trying to get that info, too.

cnlohr avatar Aug 09 '16 13:08 cnlohr

Hmm, I don't know how to get the info you need the best. It was just an observation.. Maybe it could be a start to formulate exactly what you need in a separate ticket so we could start to gather what you need. I could take a look the existing code from other projects..

bitluni avatar Aug 09 '16 13:08 bitluni

Hmm, right now things are a bit stalled before we can COMPLETELY rule out full-speed... at least on my end, but I'm not sure what needs to happen arduino-end to start support of this project.

cnlohr avatar Aug 16 '16 03:08 cnlohr

Is asking @igrr for information still a bottleneck? In my experience, he is very approachable, helpful and friendly and is online on https://gitter.im/esp8266/Arduino most of the time.

probonopd avatar Aug 16 '16 15:08 probonopd

The main thing which needs to happen on Arduino end is making decisions :) For example, do we want USB stack running only in second stage bootloader (eboot) or in the application as well? If it runs from application code, we probably need to use same USB API which is used by other Arduino boards. We can probably reuse their code, but need to decide what subset we want to support. If it runs in bootloader, do we use some fixed delay there? Or maybe we can detect if USB cable is plugged in and avoid delaying if this isn't the case? Also, do we want espusb code to be updatable over usb? Do we want the update to be fail safe, or can we expect the user to fall back to serial upload if things go wrong? Answers to these questions will determine implementation on our end, which in case of bootloader-only espusb support should be quite straightforward.

Regarding the espusb code itself, I think it would be nice to package all usb-related code into a self-contained module (static library) with a well-defined API. I can understand that some hacking and exploration needs to happen first, but once they are finished some cleanup and refactoring would be much welcome. That would make it easier for others to use this project within other applications/frameworks as is, for example by adding it as a git submodule.

Regarding additional info about hardware features of the 8266, i think Angus has already given a series of replies on the 8266 community forum. If there's anything else you want to know, please ask.

igrr avatar Aug 16 '16 15:08 igrr

Thanks @igrr for chiming in. I am by no means an expert so in the end it is @cnlohr and @igrr making the decisions, but since I started this thread, I would like to offer my perspective from an user's point of view.

Why do people love Arduino? Because it gets us producive extremely fast, without having to worry too much about the low-level infrastructure. Hence, I see 2 main use cases for espusb in esp8266/Arduino:

  1. As a user writing a sketch, I want to be able to attach an ESP8266 to my PC for flashing without the need for a USB-to-Serial bridge (just like I can attach and flash an Arduino Leonardo). It is desirable for this to be as robust as possible, meaning that whatever bad sketches I flash, I can always be sure that USB flashing will still work. Ideally I install the code that does this only once (via another flashing mechansim such as serial or a flash chip programmer) and from thereon can always only use USB for flashing sketches. I don't have the desire to flash the flashing code itself over USB (as I assume once it works I will never have to worry about it again - 99,9% of Arduino Leonardo users will never have looked at the code that does the USB flashing). I do not want to "pollute" my .ino sketches with this functionality (like with Arduino Leonardo, where I don't have to change the sketch specifically to make USB flashing work).
  2. As a user, I want to be able to use USB functionality inside my sketch (e.g., to make it act as a CDC device, USB HID device, or whatever). For this, I want the code that sets the USB device IDs and all the rest of the functionality in my .ino sketches (possibly using functions that look like https://github.com/gloob/vusb-for-arduino).

Whereas it would be elegant to have the code for 1 and 2 combined in just one place, I wouldn't worry to much about having 2 separate copies of the USB code (one in the core/bootloader, one included into the sketch), since with 4 MB of flash, who cares.

probonopd avatar Aug 16 '16 15:08 probonopd

@igrr I wish I had an opinion on the subject. I really don't know what's best, either, regarding where the bootloader should be, and the timeout, etc. I would be concerned to let it boot too quickly, since it may take a bit for the device to enumerate and a PC task to catch it if you want it to stick around in the bootloader.

I tried finding some spec for usb bootloading and didn't find one. I still much prefer use of control messages over just about anything else.

I am going to continue refining the main espusb project, now that I am putting the full-speed stuff on hold, indefinitely (unless espressif comes back with something). I have some updates just committed in 90e8a22. I will be splitting the code apart so the per-device stuff is separate from usb.c. Right now, endpoints are handled externally, but descriptors and custom control messages are in there. Most of this can be done in parallel - both work on Arduino and my work. Especially if it results in specific requests for changes to my code.

P.S. It's currently ~8.1 seconds to flash to the scratchpad via usb and another 2 seconds to copy it over and reboot.

All that said... I am not a fan of CDC devices. And, I don't even know if they're supported on all OSes in low-speed mode.

One other thing that is convenient. I do have code now that causes the bus to re-enumerate.

cnlohr avatar Aug 17 '16 02:08 cnlohr

I am getting very close to what I want for the "library"

Right now, usb.h, usb.c, usb_asm_1bit.S and usb_table_1bit.h are all librariezed. All of the descriptors, custom behavior, etc. is located elsewhere. Current usage:

Total SRAM: 232 bytes + Descriptors, 317 bytes (Could be stored elsewhere) Total Flash/IRAM (Only iram, tables and usb_init can be stuck in slow flash): 1422 bytes

If only a bare minimum USB device is needed, could be smaller yet, but not by too much.

cnlohr avatar Aug 18 '16 06:08 cnlohr

Ping @igrr your input is appreciated.

probonopd avatar Aug 19 '16 19:08 probonopd

For the bootloader mode, this amount of RAM is not an issue, while for the application the amount of iram required may be a problem. We don't have many options for stuff to move from IRAM to flash to free up some space.

Next step would be to integrate espusb files into bootloader.

igrr avatar Aug 19 '16 21:08 igrr

Would we do it where I have some code that just all lives in IRAM - then somehow, when it decides to boot, it loads the program off a different place on FLASH into IRAM and boots it?

Right now, I do use library calls for a few things like memcpy, and setting up the inputs to be GPIO instead of special functs.

cnlohr avatar Aug 19 '16 21:08 cnlohr

I can add esp-usb to our eboot bootloader. It would be nice if there was a way to tell whether USB isn't connected at all (no cable) — and avoid loading espusb from flash in this case.

Memcpy is in rom, gpio_xxx functions are also in rom if I'm not mistaken. If there are any dependencies, I'm sure we will figure this out.

igrr avatar Aug 19 '16 21:08 igrr

We could avoid use of interrupts and just poll for USB and/or serial? Do you use interrupts in eboot?
You can determine if you're connected by looking at D-. D+ will only change when the host decides to talk to you, which does seem to happen very quickly.

One of the biggest questions would be how long to wait if you are connected to a host. May take a while to know if the user wants their code to run and pretend to be a mouse vs they just haven't gotten their flasher running yet.

cnlohr avatar Aug 19 '16 22:08 cnlohr

This is how it is done in the Arduino Leonardo board:

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. The bootloader can also be initiated by pressing the reset button on the Leonardo. Note that when the board first powers up, it will jump straight to the user sketch, if present, rather than initiating the bootloader.

Is it possible to use something similar to this existing Arduino behavior as a starting point? That is, by default boot into the sketch directly without a delay unless the reason for the reboot is known to have been triggered by the Arduino flashing procedure.

Here is the source code of Caterina, the bootloader used in Arduino Leonardo: https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/bootloaders/caterina/Caterina.c

probonopd avatar Aug 20 '16 10:08 probonopd

As mentioned above, because of IRAM usage it is likely that we integrate esp-idf into bootloader only, but not in the app. In this case we can assume that if the device is plugged in, it should wait for upload. If it isn't plugged in, it skips esp-idf completely and starts up as usual. What should we check for at D- line? Iff the device is not connected to the hub, it will be in some particular state? Re Leonardo approach: probably not relevant because low-speed CDC device isn't really a thing which is supported on all OSes.

igrr avatar Aug 20 '16 13:08 igrr

https://github.com/cnlohr/espusb/blob/master/user/usb.c#L224

I think this may be helpful!

usbcomms

So google can find this: This is a picture of a powered-off ESP8266 being plugged into USB. It takes a moment, but the host does talk to it and enumerates

Re: CDC, etc. Even a bulk endpoint, I'm OK with, but the problem I want to avoid is I just hate it when things are COM ports, so many driver issues and the like!

cnlohr avatar Aug 21 '16 02:08 cnlohr

Is there still some hope that this might happen some day?

probonopd avatar Oct 01 '16 16:10 probonopd

I keep kicking the idea around, but, there's just SOOO much. I don't even know how to do the remapping of the IRAM stuff, so I'm pretty stuck at this point. Given a framework, I can totally write the USB code.

cnlohr avatar Oct 02 '16 08:10 cnlohr

Thanks @cnlohr, what exactly do you mean with a "framework"?

probonopd avatar Oct 02 '16 08:10 probonopd

I can get messages to/from the host via USB, and detect when it's plugged in or not, but I don't know how writing to flash works realistically, where different things would live in flash, and how to actually boot into flash.

cnlohr avatar Oct 02 '16 19:10 cnlohr

@igrr can you help on the above? I think this would be a tremendous benefit for the whole platform.

probonopd avatar Oct 02 '16 19:10 probonopd

FWIW, I'd love to see support for the Arduino Serial, Mouse & Keyboard libraries on ESP8266 with this also. Not sure if that's harder or easier to implement.

blackketter avatar Oct 03 '16 15:10 blackketter

No way to do serial, but things like mouse/keyboard/joystick shouldn't be hard. Really, though, I think a bootloader is the critical point!

cnlohr avatar Oct 03 '16 16:10 cnlohr

I agree, the bootloader and firmware updates are more important, but having USB support for Arduino applications would be very useful as well.

(I do realize that the native ESP8266 serial console wouldn't be possible but an application-level serial port should be, no?)

BTW: THANK YOU for this awesome work.

blackketter avatar Oct 03 '16 16:10 blackketter

If you're using application-level stuff, you really really really should be using control messages rather than treating something like serial. Control messages allow you to say "Function 5, length 50, here's my payload" which is much better than handling framing, etc. yourself.

Back to discussion at hand: @igrr any ideas on how we could make this happen? Should I just make a super minimal make-usb-link thing that lives in IRAM? Or, is IRAM a bad thing when dealing with bootloaders?

cnlohr avatar Oct 03 '16 21:10 cnlohr

From bootloader point of view, the following functions are needed:

  • is_usb_plugged_in() should check if cable is connected
  • usb_enumerate(void (*cb)(size_t size, const uint8_t* data)) should return when enumeration is successful. Callback function cb should be called whenever host performs a transfer.
  • usb_transfer(size_t size, const uint8_t* data) should perform transfer to the host. I think this should be enough to implement firmware update.

All functions should live in IRAM, as we have plenty of it in bootloader, and we don't use flash cache there at the moment.

igrr avatar Oct 03 '16 22:10 igrr

I'll work on this shortly. I am gonna see if I can do all this with as little of the SDK as possible.

I am thinking the transfer thing might be a little easier to use via global messages, since, data going in and out has to be done asynchronously. I.e. "I got this data last transaction" and "when you get around to it, please send this back to the host" unless I can just have loop continuously on my side...

cnlohr avatar Oct 04 '16 02:10 cnlohr

Hmm, I've got most of my stuff off the ground here, but, it's like the processor is waking up at 52 MHz instead of 80. If I call ets_update_cpu_frequency( 52 ); Everything works as-expected, but I think I should be developing for 80 MHz, no?

cnlohr avatar Oct 07 '16 20:10 cnlohr