-
@Gordon These source files are great ... but they aren't just "drop in and it works" ... I understand that there are limitations, setups and considerations that we can't ascertain from just a look at the source code. I've asked the Arduino team if they have any docs or are willing to work with me to write up a "guide" on how this technique and code can be applied outside of the ESP8266/Arduino project. I'm not looking to be spoon fed ... but rather to actually talk to someone who understands how the "yield" stuff works to see if there are any lurking reasons that we couldn't use this technique. I'd hate to spend the next 2 days integrating an ESP8266 yield technology only to find that it wasn't going to work (in conjunction with Espruino) and then be told at the end "Oh yeah, we knew that was a limitation if you'd asked, we could have told you.".
-
In the ESP8266 community there are a few open source projects that stand higher than all the others. One of them is "esp8266/Arduino". See:
https://github.com/esp8266/Arduino
This project has enabled the Arduino IDE to be used to build ESP8266 apps and has also implemented the core of the Arduino libraries to run on the ESP8266. The result being that folks who come from the Arduino side of the house for C programming can feel right at home on the ESP8266.
The chap that runs that project is a genius with the handle @igrr.
One of the things he achieved is a "yield" mechanism. This allows code that is being executed as a result of an ESP8266 callback to "yield"control back to ESP8266 and when ESP8266 has finished its housekeeping (presumably very quickly) control returns back to where we yielded from. It is possible that THIS may be exactly what we need.
The problem is that the logic of how this framework works is not clear to me and I have asked the ESP8266/Arduino team to assist. And before anyone says "Why not read the code?" ... this one is written in Xtensa assembly language :-)
I have also asked @igrr for permission to include this fragment in the Espruino/ESP8266 port and he has said yes ... but I'd like @Gordon and @igrr to both be happy with that notion so I'd like to put them in touch with each other ... however ... that shouldn't stop us from investigating that part to see if the technologies are even compatible with each other.
-
-
-
In the Espruino port to the ESP8266 what I am doing is invoking jsiLoop() once and, when it returns, scheduling a callback the next time the ESP8266 is idle and giving control back to the ESP8266 runtime. When the callback informs me that ESP8266 is idle, I invoke jsiLoop() again and repeat this process forever.
The philosophy here is that the ESP8266 is a single threaded processor and hence can't do things in parallel. Since WiFi and TCP/IP are implemented in software and radio in the ESP8266, we can't take too long outside of the ESP8266 world. So far ... all has been working fine. Espressif claim that we should give control back to the ESP8266 every 50 msecs or less.
Now imagine that we did NOT return control to ESP8266 in a timely manner. Imagine that jsiLoop() took seconds to return. What that would mean is that the WiFi and TCP/IP layers would not be fed. So, for example, if we setup a socket listener in JS such that an external client tried to connect ... if we didn't feed ESP8266 soon enough, an external client might try and form a connection and its own TCP layer wouldn't get any IP level responses in time and the connection would appear to fail.
We are starting to see indications that we might be not feeding the ESP8266 fast enough. It is manifesting during timing critical areas such as WiFi access point connections. If we ask the ESP8266 to connect to an AP as a station while also having setInterval() timer at work, we appear to get WiFi level timeouts.
It is early days in debugging this. If we do NOTHING timer related while forming the WiFi AP connection we are good ... so that might be a work around ...
Now ... the meat of the question. Is there a description of how the main loop of Espruino works? Does it process a statement, a function or something else in a unit of jsiLoop()?
Neil
-
As the port of Espruino to a particular board continues, a lot of questions have come up on implementing the board specific portion of the networking functions. To attempt to understand this area better, a first pass at a document has been written that attempts to describe the board specific functions. This document will be elaborated on over time and eventually submitted to the Espruino project when the porting project has completed.
-
From an ESP8266 perspective, EVERYTHING it does is asynchronous. When you write an app for an ESP8266 you are given one time control in an initialization function and from there you set up everything you need. Following that, everything is handled by callbacks. For example, if we wish to connect to an access point and then make a socket request ... the code in ESP8266 would make the API call to connect to the access point and then return. When the connection to the access point completes, our user code is called back to say it is done ... and there we would request a connection to a partner host ... we would AGAIN return control to the ESP8266 and when the connection is finally established, we are called back again ... where we can send data. Following the request to send data, we AGAIN return control to ESP8266 and when the data send is complete, we are called back once more.
So from an ESP8266 perspective, we make requests for ESP8266 to do something and then we pass control back to ESP8266 which tells us when done (or when an error occurred).
For the Espruino port, there is the concept of a "main loop" in Espruino (jsiLoop()). What we do there is:
MainLoop:
- Call jsiLoop()
- Schedule a callback to MainLoop when next ESP8266 idle
- Return control to ESP8266
So basically what is happening is that we do one cycle of non-block Espruino JS work, register that we get called back when ESP8266 is idle and return control back to ESP8266. It does what ever it needs to do to keep itself alive and process network requests that are outstanding (which may be nothing in most cases) ... and then main loop is called again and we repeat.
It all hinges on Espruino being non-blocking. The rules of ESP8266 say that we can't starve ESP8266 execution for more than 50 msecs of elapsed time. So it is essential that any one pass through the Espruino processing loop be 50 msecs or less.
- Call jsiLoop()
-
@Gordon ... awesome ... I understand your response and those were the designs I needed. As for telling me how to implement every function ... I don't disagree. You ARE the whole Espruino architect ... you OWN the grand design and vision. Our contribution is time, ESP8266 skills, ESP8266 coding, debugging, and testing. Right back in the original posts on the ESP8266 port, we recognized that our challenges on this port would not be ESP8266 knowledge or time ... but Espruino internals knowledge.
I'n my 9-5 job, I'm a grunt programmer and used to working with designers and architects. Those guys own the knowledge and vision and ... the important part ... architecture. Together ... we work on a project ("Port Espruino to ESP8266"). We identify work tasks ("Implement networking"), we assign responsibilities ("Kolban - go own 'implement networking') and then I buckle down to figure out how it can be done. However, there isn't a day that doesn't go by where I don't have a question or problem that I bring by the architect who points me in the right direction. He doesn't "implement" what my job is to build ... he guides me on the architecture and, just like you did in this post, he may help me with pseudo code and drawings. The grind work then comes back to me to take what he has given me and from there ... low level design, code, test, debug, document.
This is my way of saying how grateful I am that you take the time to respond patiently to these forum posts. Maybe that last post of yours took 10 minutes or 30 minutes to compose ... but I anticipate that my work now will take a few hours to get right and test and document ... and if I do it correctly, there will now be a paper trail for those who come next.
It is also vital that we not abuse your time and grace. If you ever feel that we are off track PLEASE let us know and we will throttle down questions ... or simply delay in responding to them till your time permits. If there are others in the Espruino community who have knowledge on the networking subsystem even close to Gordon, please make yourself known :-)
Neil
-
@Gordon
As always ... awesome answers. Based on what you've said, I think I can make some tests tonight. The answers for connect() were perfect.In a lot of your answers, you have invited me to go look at code ... and being pointed to the correct code is GREAT ... however ... when you say something should be obvious, you are over assuming my skills. I have and can lay my hands on ESP8266 skills as needed but Espruino internals are a "grind". That is definitely NOT to say that they are poor in any way ... just a lot of stuff that I think is all locked in your head. Without your responses on the forum, this port would be dead or dramatically further back than where it is.
Neil
-
When implementing a new network component for a new board (eg. ESP8266), we have to create a function that is mapped to the JsNetwork
send
function. This is a function with the following signature:int send(struct JsNetwork *net, int sckt, const void *buf, size_t len)
What I feel I need to know are the semantics and contract for this function.
What I sense is that, in its simplest mode, it is supposed to send
len
bytes of data starting at the memory pointer supplied bybuf
through the socket identified bysckt
. It should return the number of bytes actually sent, 0 if no bytes were sent and -1 if there was an error.Unfortunately for some devices such as the ESP8266, this semantics needs further consideration. In the ESP8266 the correspond function to transmit data is called
espconn_send
. However, since nothing in ESP8266 is blocking, this call takes a buffer of data and then will eventually transmit it. When the transmission has completed, a registered call-back function is invoked to let us know that the data has been sent.Now ... let us try and map this to the apparent Espruino architecture. A naive implementation of the Espruino
send()
would be to simply callespconn_send()
however what should we then return? If we return the length of data thatsend()
was provided, then we are semantically declaring that all the data was transmitted which isn't the case. In fact, it appears that immediately following a successfulsend()
using HTTP, we then close the socket.The next possible design thought is that when
send()
is called, we do invokeespconn_send()
but return 0 ... indicating that NO data has been transmitted. The assumption here is thatsend()
will continue to be invoked over and over. The ESP8266 layer could be watching for the previous send to complete and, when it does, we return that the data was sent. This may work, but makes a broad assumption. It assumes that whensend()
is called with some data and gets a return of 0 ... it will continually call with the SAME data to be sent over and over again ... it isn't clear in the semantics if this is correct or not.Another open question is the buffer passed into the
send()
call. Should the content be copied from the request or can we assume that the memory pointer is "good" and "stable" until the data has actually been transmitted at some time in the future. If we can't assume that, then we have the unfortunate circumstance of having to duplicate the memory buffer to hold a copy while we transmit.As we see ... we are starting to ask complex semantic questions which weren't immediately obvious from such an innocuous looking function such as
send()
.Neil
-
I wanted to report back that your technique works well. What I think we want to do is put our heads together and capture and document the EXACT nature of the contract between the socketserver layer and the board layer. Specifically, each board must implement a set of expected functions. What we need to do is pin down exactly the nature of the contract including semantics. The code source code of the Espruino base and examining the examples describes the signatures of the functions ... but doesn't capture the execution flow nor semantics and I think (in hind sight) that is what gave me most of my grief. If we can learn from this and make notes on what each of the board supplied functions does and its expected contract ... that would help a lot.
I have found yet another blocking vs non-blocking issue which is going to be tricker to resolve ... and that is "gethostbyname" which takes as input a DNS name and returns the corresponding IP address. The ESP8266 has this ... it is called espconn_gethostbyname ... however ... it is asynchronous. What I mean by this is that its spec is that when one calls it, one supplies a callback function that will be called in the future when the DNS resolution is complete. In the network and serversocket code, we expect it to be blocking.
-
-
@Gordon,
You are absolutely welcome to lift any and all of the docs in the Wiki. I was thinking that at the end of the ESP8266 port project, they would be offered to you for assets for your disposal but if you want them now ... GREAT!! ... however, if you lift them now, tell me which files you lift and I'll delete them from the ESP8266 repository ... don't want to have duplicates. I saw the posts from Mr @mjdietz and have reached out to him in case we can collaborate on experiences building boards and share common experiences and questions in case you are not available.My work day is nearly over and I'm excited to test out the connection callback designs :-)
-
Howdy @Gordon
Great post. The primary goal is to ensure that you are 110% happy with the final inclusions that will be in the Espruino code base that you own. When the ESP8266 port is finished, as I say, the parallel Github repository will be destroyed (after harvesting things like the docs).
Let me try and provide thoughts on your comments and questions.
At a high level, a PRIMARY goal of the project is "do no harm" (just like a doctor). There is a snapshot of the Espruino project here:
https://github.com/esp8266-espruino/esp8266-espurino
This is not a fork but a "sand box" against which all development is progressing. This provides a rock steady base for ESP8266 specific work. Anything you or others may have "in-flight" will not impact ESP8266 specific work. But (I hear you say) ... what about changes that are made by the ESP8266 port to the base Espruino code? The good news is that so far ... those have been minimal AND ... each of those I have submitted to you as changes in a "master" fork which you have accepted.
So ... when the time comes to "merge" the ESP8266 port with the master Espruino code base, my hope is that the ONLY final changes will be the inclusion ESP8266 specific project files that will be delta NEW to the master code base with ... ideally ... ZERO changes to the rest of the code base that is not ESP8266 specific.
There are a couple of MAJOR exceptions to that ... which are to do with the build system. The ESP8266 port will introduce new "board files" and also significant additions to the Makefile.
The thinking is that on merge day ... there will be a "pull request" awaiting you which will add the new ESP8266 source files AND changes to the Makefile ... and once you approve ... we will be done. The changes that I find that are necessary in the core Espruino base ... these have been filtering in to you already for your review and approval.
So what will we loose following this mechanism? The answer is the file change history for the new ESP8266 files that don't yet exist in the master repository ... but since those are net new anyway ... that shouldn't be a problem. The history just now is ... well ... useless to you and me.
For the Wiki documentation ... I am huge on documentation. I don't do anything without writing a story behind it when done :-) ... For me right now, the documentation is easiest for me to capture in Wiki format. The new ESP8266 source files are being liberally commented internally. As for the generated end user docs ... you are 100% correct. When the files are provided for merging, they will be updated to include the docs necessary for "build_docs.py" (is the hope and intent).
I fully agree that a staticmodule of ESP8266WiFi is wrong and will indeed change to making it a library. That is the kind of polish that we need to capture before completion. In the original Proof of Concept, we used a staticmodule and ... so far ... haven't gotten to the stage of reworking to a library.
As for the naming of functions ... I fully agree with you that before completion, we should all get together and review for discrepancies and improvements. Today, I am using the names that are the ones used in the ESP8266 SDK as opposed to ones which have heritage in Espruino ... but by merge day, we should all be consistent.
As for the project as a whole ... once the Proof of Concept proved that Espruino running on ESP8266 was feasible, the next task was to approach it formally with a primary goal of building something that could "slot into" the primary Espruino project. While the naming and the docs are still not where either of us want them to be, the focus just now remains on getting the function working. As you know, the ESP8266 port is still currently broken on the connection callbacks and your post earlier today gave us the knowledge to attempt to rectify that. It is things like that that have my highest priority. I break tasks down into:
- Tasks that we know how to accomplish and merely need to expend time to do them.
- Tasks that we have unknowns upon which include designs, bug fixes and functional testing.
It is the items in category 2 that need most of our attention. The items in 1 can be accommodated before merge day and are merely chores. Items in category 2 are the things that keep me up at night.
The Espruino port to running on the ESP8266 is not complex ... where folks like me are having problems is in understanding how to augment the fantastic Espruino architecture. The core knowledge is locked up in your head and it is only by forum thread exchanges and deep study of the existing code that folks like me can begin to make progress. You have so many irons in the fire, I realize that the ESP8266 port can't commandeer too much of your time so your prompt response on the forum are awesome. If you do have time however, please please don't hesitate to contact me directory (kolban1@kolban.com or skype: neil.kolban) should you have spare time to give us architectural direction and guidance in a more direct fashion.
And ... less there be any doubt. The Espruino code running on the ESP8266 is 99% your current and existing code with the remaining 1% being the ESP8266 specific code ... and that speaks VOLUMES to the quality of what you have created.
Neil
- Tasks that we know how to accomplish and merely need to expend time to do them.
-
Howdy @JumJum
Great news that you got it running. But lets make sure that we realize that the coding is no where near finished. I loosely target November for a more functional and stable proper release. Things are known to be broken all over the place including things like network connections claiming formed before they are ready. GPIO testing has also been minimal and there are likely to be memory leaks to be fixed and optimizations to be made.
With these words of caution ... I don't want folks to NOT play with it ... but to tire kick it and report bugs ... but don't take its current state as a statement of its quality into the future ... it will get better :-)
Neil
-
@mjdietz
Many thanks for this post. I need this knowledge for the ESP8266 port but hadn't got there yet. Awesome. Feel free to contact me directly anytime if you want to shoot the breeze on Espruino porting ... maybe we can help each other above and beyond Gordon's awesome support.@Gordon
Nice post and detailed response ... yet MORE stuff for me to study :-) -
Gordon,
Aha!! The light bulb just went on for me after reading this post. Tonight after work I'll be studying this in GREAT detail but I think I see what you are saying. Basically, at the ESP8266 networking level that is SPECIFIC to the ESP8266 (i.e. the lowest level of the Espruino stack and the board specific code) ... THAT library should maintain state on socket connections and when the higher level functions ask ESP8266 to do something ... even though the connection has not been formed ... that socket data structure will have private STATE (and this is where I missed the key point) ... it will return 0 for sends and receives ... but when the ESP8266 (actual) connect callback returns, that will change the state so that subsequent sends and receives will now be honored.Again... THANK YOU for your extreme patience. I'm going to try and write this story down for others who follow as well.
Neil
-
-
@Gordon,
I'm afraid I'm not following the meat in post #8. In JavaScript (pseudo code), I want to execute something like:esp8266.connect("IP", function(handle) { handle.send("Some data", function() { handle.close(); } });
This would request a connection ... when the connection is made, it would send some data ... and when the data has been sent ... perform a close of the connection. However, since the current base code invokes the connection callback immediately after asking for a connection ... this is before the connection has been formed and we are not yet ready to perform a
send
operation. In the ESP8266 world, theconnection
operation (at the ESP8266 SDK level) is always non blocking and one supplies a callback function pointer (in C) that will be invoked when the connection has been completed. It is then that I would have imagined that thenet.connect()
callback would have been ready to be called.I'm not seeing how post #8 changes that story....?
Neil
-
Seconding what @Gordon said, @aplikatika is the father and originator of this porting project. When I got involved, the port was already functioning at the GPIO level and at the JavaScript level just fine. We consider the first work a successful proof of concept and like any proof of concept, we will do anything to get it working. This second phase is the slow, methodical drudgery of detailed architecture mapping Espruino models of the world to what is meaningful on ESP8266 and adding as much polish and support as possible before the 1st inclusion in Espruino proper.
This is an open source port and once done will be fully merged with the master repository (assuming it passes all the necessary quality tests). At that time, the Espruino port to ESP8266 project as a separate entity will come to an end and the GitHub project deleted. What will remain is a single project (Espruino) where the good folks who worked on the original will still be available for any ESP8266 specific issues.
VERY early testing of a binary distribution has started. See the following:
https://gitter.im/aplikatika/Espruino-on-ESP8266
For the last couple of days (2015-09-21 - 2015-09-22). This is NOT a release. Again ... it is NOT stable and should only be attempted by those who are super interested. However, we do need testers who will validate that the basics are present and also so that we can start making fix lists.
-
Excellent ... thank you my friend. As I was studying the GPIO subsystem yesterday and making notes I was writing up the following:
https://github.com/esp8266-espruino/esp8266-espurino/wiki/68-Espruino-GPIO
So will merge your words into that. At the end of the Espruino-ESP8266 board making exercise once we have merged into the master, that project will be taken down an destroyed ... however, before that ... we will decide what (if anything) to harvest from the notes I have been taking and maybe bring those into the master project too ... if that is useful to you.
-
As I work upon the ESP8266 port, the time has come to provide some attention on the GPIO mechanism. When one defines a new board, one creates a Python script with the board name under
/boards
. It appears that within this script, we can define a variable calleddevices
as well as define a function calledget_pins()
. What I am hoping I can get is some boot strap information on these. In return, I'll write up a users guide for those who may follow. Let's start withget_pins
. What is that supposed to return when called? -
-
I think we have just achieved a milestone. We now have an ESP8266 running a simple HTTP server where the JavaScript to do that should be the same generic JavaScript as would be used for other Espruino boards.
The sample JavaScript used is:
The latest binaries of the firmware as of 9/25 onwards should be used for testing. See here for URL information:
https://github.com/esp8266-espruino/esp8266-espurino/wiki/07-Samples