-
• #2
I'm afraid it would involve some pretty major software changes to Espruino to get it to work. On the hardware side, the 5th pin on Micro USB isn't brought out anywhere, and to do it properly you need some power switching arrangement as well to supply power to the client device.
Basically doing it that way would be really difficult.
However, this has been asked a few times before, and there's a chip that will handle USB Host for you called the MAX3420/MAX3421: http://forum.espruino.com/conversations/284427/
It's available on a board with everything you need from a few places. A quick search on eBay for
arduino usb host
shows you can get them for under $5 (although I'm not 100% certain those use the MAX3421) - so not much more expensive than a USB OTG cable.There's no code for it for Espruino as far as I know, but the example code looks like it would be trivial to port over and I'd be happy to help if you have any questions about it.
-
• #3
I have order a device. Once it arrives, I will see how far I come.
-
• #4
Great! It'll be amazing if we can get this working in Espruino - it opens up a lot of possibilities, especially as it should work on all platforms.
-
• #6
cool, just did a quick search:
https://github.com/aoabook/Arduino/blob/master/libraries/UsbHost/Max3421e.cpp
this looks like a nice base to create a module in javascript. -
• #7
There's also an original repository from the board I am using. It comes in 3 versions, but v1 library looks good enough for the start: https://github.com/felis/ArduinoUSBhost/
The authors homepage also contains a good manual: https://www.circuitsathome.com/usb-host-shield-hardware-manual/
Everything else can be read in the original chip specification: https://pdfserv.maximintegrated.com/en/an/AN3785.pdf
I have already started to get into it, but it seems I can't establish the SPI connection. I'm sure that I have wired everything correctly (tried both with SPI1 and SPI2), but whatever I send, I will always get back a 255 so far.
Does this maybe ring a bell for someone? If not, I'll try to connect my oscilloscope tomorow to debug it further.
-
• #8
A long sleep did help to find out that I did not connected everything correctly. The cheap board from china had one label printed one pin too far to the left. Eventually I even bricket that board, but that needs to be tested.
-
• #9
I guess the board is really dead as there is no change on the MISO pin regardless what I do. It's always HIGH. I'm not sure what the wrong pin did, but maybe the second board I ordered will work. Hopefully it will arrive soon as I'm now into it.
-
• #10
Luckily, the device wasn't dead an thus I was finally able to make it work now. I have created a pull request to get the basic header translation for the MAX3421 ready. It's not much useful so far, as the USB enumeration and such still has to be done from external code, but at least it is now easier to get started.
-
• #11
That's great - thanks! I'll leave off merging your pull request (https://github.com/espruino/EspruinoDocs/pull/502) for a few days though as it seems like it's possible you or someone else might actually be able to get it communicating with a simple peripheral like a keyboard pretty soon?
-
• #12
A wise decision. While the MAX3421E module is mostly working, it is mostly not just more than this. This means that it doesn't do much USB handling on its own so far. And for this reason it's yet untested (as there isn't much outer code available so far).
The original author and developer of the breakout board has decided to split the code into some files for accessing the MAX3421E chip on the one hand and files for USB handling on the other side. To me, this makes sens as it allows to create independent USB access solutions. As far as I have seen they can range from simple to complex and think it doesn't make sense the add more overhead than necessary.
So in my opinion, it would make sense to have this small MAX3421E module as base and several other libraries (separated by purpose) on top of it.
For the moment, some example code would probably be satisfying. Something that could later be expanded to a module. However, I fear, that even a simple device such as a keyboard, might at least introduce quite some more basic code, as all the USB enumeration and accessing needs to be done.
It's in the range of about 2-3 days of work, but personally, I can not make it this week. So you probably want to keep it open a bit longer, maybe.
-
• #13
Thanks! The separate module makes a lot of sense. I guess maybe any common bits of USB handshaking could be put in the main module, but yeah, it seems a lot more sensible to have keyboard/mass storage/etc modules. Who knows, maybe later on they could be used with other USB host interfaces :)
I'm not 100% sure on this but I believe keyboards have a 'boot mode', or at the very least most of them use a very similar packet format. You may find there's very little code needed to actually get something working if you try those first.
... I don't have time at the moment, if/when I do then I'd love to build on what you've got there and try and get it supporting some other peripheral types.
-
• #14
I already connected an old keyboard to my test setup, but didn't found the time so far to go further than that. In fact - as you mentioned - some basic module for USB Host handling could be written easily. From there it should be fairly easy to get some keyboard demo working.
There are just a few questions left from my side:
Can I require another module from within a module? I have found something like that in the touchscreen example, but I'm not aware issues with this approach. The touchscreen example implicitly pre-connects the ADS7843.
Rather than this, I'd like to have something like passing an interface to the usb-host module. The code should look something like:
var USBHost = require("USBHost").connect(require("max3421").connect(SPI1, A0, ...));
in the end.
Within the module I would need a way to determin what module has been passed. If it is of type max3421 it use this internally.
This would have the advantage to write the code of USBHost as independend and reusable as possible.
Alternatively, I could write a module named USBHostMax3421, which kind of inherits from Max3421. The initialization would look like this then:
var USBHostMax3421 = require("USBHostMax3421").connect(SPI1, A0, ...));
This is probably easier in the beginning, but less flexible in the long run.
Also I'm not sure how to handle interupts and gpio querries. The original arduino code uses a loop to check periodically for 'tasks'. With Espruino this translates to watches, doesn't it? If I use them, are there known issues in regards of the use in modules?
And one last question. The original code uses delays at some point to wait that some capacitors are fully loaded. So far I have translated the code directly using some time burning hack. While the time is very little (60ms), I'd like to get rid of this. Now I wonder whether there are better alternatives?
The only alternative that comes to my mind is using callbacks like:
init(onSuccess, onFail)
but that bloats the caller code and requires some more handling on that part.
Or should I use promises? While they look very smart, I tend not to use them in my web projects, because they used to be not fully supported by some browsers.
-
• #15
Can I require another module from within a module?
You can, yes - but if you require multiple modules then multiple ones will be included, which probably isn't what you want.
var USBHost = require("USBHost").connect(require("max3421").connect(SPI1, A0, ...));
This is probably what you want... If it's an issue it could always be shortened to
require("USBHostMax3421")
with another module later on as you note.I'm not sure how to handle interupts and gpio querries
Yes,
setWatch
is fine - an no real issues with modules, no.delay
For 60ms I'd say setTimeout and callbacks would be preferable (I really try not to use hard delays for anything more than a few milliseconds). I tend to use
callback(err)
so if error is undefined it's ok.However promises might be good. They're fine on Espruino and while they might be overkill for the 60ms delay, you may find they actually save you loads of time and effort when you're looking at doing the actual USB negotiation stuff - and if you use them for everything the the API it'll make it seem much cleaner.
-
• #16
Today I have advanced the development, but I guess I'm running out of memory. At least if I want to write the library very clearly (that is with meaningful variable names and constants).
In particular the constants seem to eat a lot of memory, but if I do it without, I probably loose track pretty soon as the USB specification isn't really trivial.
It would help if there is a way that the minification could replace constants by its value. So that "C.USB_SETUP_RECIPIENT_INTERFACE" would become "1" (its value).
In the end these names would be necessary as the user would need this as well in order to customize the code for dedicated USB devices.
What I have done so far is to start initializing the device and querying some basic device properties (description). Still very basic, but some constant progress.
I think I can gain some memory, by switching from the original espruino to the pico, but that would only solve it for the moment.
-
• #17
Thu 2019.04.18
Does this article get over that obstacle?
In case there isn't a Pico lying around. . . .
>process.memory(); ={ "free": 3413, "usage": 1687, "total": 5100, "history": 2055, "gc": 6, "gctime": 7.46250152587, "stackEndAddress": 536958468, "flash_start": 134217728, "flash_binary_end": 381336, "flash_code_start": 134234112, "flash_length": 393216 } >
-
• #18
I think the problem is that I need it for development. Quote:
Because this requires preparing the EEPROM in advance, it's utility may be limited to code which does not change often.
As this is work in progress, I currently need to test it often.
To get around the issue for the end-users I think I need to break the developed modules down in the end. This will certainly break the modular in favor of something like a "max3421UsbHostHID" or "max3421UsbHostStorage" or whatever is needed. Then I can remove unused constants and/or us the constant values directly.
The user will have less options to get their hands on the USB protocol, but at least they can use the devices (otherwise they might run out of memory just for connecting the external USB device).
In my case I would also need to have some code on top which takes care of processing the data I retrieved over USB.
-
• #20
The original espruinio only has a total of 2240 variables
>process.memory() ={ "free": 2220, "usage": 20, "total": 2240, "history": 0, "gc": 0, "gctime": 4.05120849609, "stackEndAddress": 536910432, "flash_start": 134217728, "flash_binary_end": 237128, "flash_code_start": 134459392, "flash_length": 262144 }
whereas the pico has 5100.
While I own 5 Picos, all except one are in use currently. And the one not in use does not have the right headers (pins vs. sockets) to connect it straight forward. I either have to solder new headers or get new connecting cables. And/or rework the code so that it can live with what it offers.
I also have a ESP32 dev board somewhere, but I do not have much experience with ESP32. There might be other issues.
-
• #21
After some refactoring and with the "direct to flash" option, I do now have enough ram available to keep going. And if not I can still switch to the pico...
-
• #22
Have you tried
minify.js
with ADVANCED_OPTIMIZATIONS?activate compilation_level = ADVANCED_OPTIMIZATIONS by
touch espruino.externs
and than run
node <path to >/EspruinoDocs/bin/minify.js MAX3421.js MAX3421.min.js espruino.externs
-
• #23
Or start with
node /Espruino/repos/EspruinoDocs/bin/minify.js MAX3421.js MAX3421.min.js
for
compilation_level = SIMPLE_OPTIMIZATIONS
-
• #24
Hmm - when minifying,
C
should get 'inlined' and removed. That's the idea anyway - and it's what all the normal Espruino modules rely on.One thing that would stop it is if you exported
C
- then the minifier decides it is something that can be changed and that it can't mess with. You could try splitting yourC
into smaller objects - one forREG
, one forMASK
, etc - and that could help the minifier (or at the very least help you to narrow down what's taking the space).After some refactoring and with the "direct to flash" option, I do now have enough ram available to keep going
The
C
object itself will take a bunch of space (probably at least 2 vars for each constant entry). However if (and you probably only want to do this for development) you madeC
a 'getter' function that returned the object, it would take basically no RAM until it was executed - obviously it's a lot slower though. -
• #25
I managed to get the MAX3421E chip working with the Pixl.js last year and have it talk to a device with a CP2102 chip. When I came across this thread today I was finally motivated enough to write it up and post the source code. Hope it helps!
What is needed to support USB-OTG with the Espruino?
Background: I have a device with an USB-OTG interface. If connected with the computer it act as slave. I can open a (serial) console and send a short command (string).
Now, I would like this to happen from an Espruino powered device (can be any board).
I figured out that the cable decides who takes the initial host role by a 5th pin. Beyond that the role can be changed from software as well.
While I don't need the latter, I just want any working combination so that I can send out a string over usb (just like as it was connected by a console).
Would it just work to prepare a cable to set the initial host/slave role to make the Espruino be slave? And what is needed to make the Espruino be the host (if possible at all)? Do I have acces to the additional 5th ID wire (actualy it's the 4th wire, labeled USB_ID in the schematics)?