-
Notifications
You must be signed in to change notification settings - Fork 492
Conversation
Initial copy from google doc
>>GET /user/aturing | ||
<<39 { "first" : "Alan", "last" : "Turing" } | ||
|
||
##GET_VALUE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@proppy
First place that might be controversial. I think it is important to not force the user to parse json. I would almost be inclined to not include the primary "get" at all. But I could see it as useful for some users.
I feel that a goal of this project is to provide cloud backends for non-cloud users who have sofar stayed local or offline due to a reluctance to face the learning curve of cloud tech. json isn't a common protocol in the embedded world and is relatively hard/expensive to decode.
##INIT | ||
Must be called after creating a Serial connection, it can take either just a host for accessing public variables or you may also provide a secret for accessing protected variables in the database. | ||
###Usage | ||
INIT $Host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we could have that be FIREBASE $HOST $SECRET
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm not sure about that one, INIT implies that it needs to be called everytime to initialize the connection.
Maybe CONNECT $HOST $SECRET?
Just It seems these should all be commands to the system and phrased as such where FIREBASE isn't really a command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking that if the CHIPLET was programmed with multiple APIs, it might expose similar commands derived from REST verbs over different APIs (hence the explicit 'FIREBASE').
But that may be a premature discussion, what about 'BEGIN' to make it Arduinoish?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah got ya. Hadn't considered the other APIs. BEGIN works too, we can do BEGIN_FIREBASE if we want to leave the option open for other APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do only begin for now ;) I agree we should worry about multiple API when we have more than 1 binding ;) sorry for the disgression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then what about:
API firebase version
BEGIN someurl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not really sold on adding an extra call. I think the fewer calls you need to make to get the env setup to start communicating the better.
I guess for versions I would just change the begin call. i.e. create a BEGIN_FIREBASE2 if we ever need to implement a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I liked about the API
prelude is that it makes it feels declarative. Like an #include
or an import
. Later we might add some extra calls that allow you to manage the lifecycle of the API
s loaded in your chiplet: list version, OTA, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright I am on board with something like that. We can have a concept of "default api" that will load at startup.
So for now we can just leave this out all together and address it later on when we introduce other APIs or versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright I am on board with something like that. We can have a concept of "default api" that will load at startup.
Yes, so for now I think it's fine to just have BEGIN url
/cc @aliafshar |
remove GET_VALUE
$DATA_BYTE_COUNT $DATA | ||
###Examples | ||
>>GET_VALUE /user/aturing/first | ||
<<4 Alan |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another issue with having the size as TEXT
is that is also a variable length unless we do some padding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true but you can just read until whitespace. there is also Serial.parseInt()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then what's the improvement over reading until \n? That you're guaranteed the size will never cross a given threshold (a few digits).
Maybe this could come in the same kind of stateful MODE
s than type prefix and forced json strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you know on the outset the size of the value coming back you can prepare for it. For example you can allocate a buffer large enough to hold it without having to allocate a smaller buffer and grow it. You can also quickly decide if something is too large to process and close the connection.
We could also return text without the json escaping which might be nice so the user doesn't need to unescape it themselves. For example if they wanted to send it directly to a display.
|
||
##GET_BULK | ||
Same as GET but returns value with size prefix. useful when value is expected to be large so you can know the size before accepting value. | ||
Also only returns values at leaf nodes, if called on internal node returns error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking that the bulk MODE would return the data always as it is coming from fbase (json escaped), otherwise it's a strict subset of GET behavior.
Maybe we need separate flag/mode for json/size/type and keep the default very simple and readable (leaf value, un escaped, no prefix), wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am ok with just dropping the bulk mode for now. We can add it back if we see a need for it in practice.
I do think the return from GET needs a prefix though, to allow reliable testing to see if it is an error or valid response before you try to parse it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love if we added all of those as optional stateful flags you could turn on and off so that we can experiment with the wire protocol and see how it feels without adding all the different combination as separate commands.
Then we can debate which one is the default, but everything you listed sounds nice to have:
- type prefix
- size prefix
- decoded leaf value
- disabling non leaf value
After thinking more about it I think you can disregard most of my comments wrt to optional type prefix. It would introduce ambiguities in the protocol to make type prefix optional if we unescape leaf values: client won't be able to distinguish an error from a string value that start with |
I also really like the Redis protocol doc you linked http://redis.io/topics/protocol. I think most of it is directly applicable here and we could easily derive from it with a few caveats:
|
##Response Format Byte | ||
All responses will be prefixed with one of the following bytes signifying the response type. | ||
``` | ||
+ If response is ok and a raw value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also have something for raw number and booleans ( maybe :
and ?
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
+ If response is ok and a raw string value. | ||
* If response is ok and a raw string value prefixed by count of bytes in response then new line. | ||
# If response is ok and a integer value. | ||
. If response is ok and a float value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
json doesn't have a difference between float and int: so maybe just :number
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
json doesn't but I think there is a lot of value in providing the distinction for c++ parsing. So you can do a atoi() call with confidence knowing that the string will parse. I think we can determine pretty easily in the library (check for a '.').
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be down for always return float, so that we can always use atof
on the receiving size.
We never know if something is intended to be stored as an int, or as a float on the firebase side: it might just be that the current intermediate value is a round number. It seems easy enough for the client to cast it back to an int when needed (or maybe we could expose a typed GET
that always return int).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see where that could be a problem if the value changes from float to int due to chance. Alright I am onboard with leaving the ambiguity up to the user to resolve. Just label them a "number", the user will probably know with domain context what to expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that's why I think we might just need :
for number and just return what we get from firebase.
I think we have reached consensus here. Are there any more outstanding issues or does this protocol look like a solid starting point? |
###Usage | ||
GET $PATH | ||
###Response | ||
$DATA_AT_PATH |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be +DATA_AT_PATH
? maybe add a separator between the two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the confusing part of using "$" for bulk strings. I am using it in all of the documentation to represent a variable (ala bash). So this should stay variable "DATA_AT_PATH" because we cannot say what type it will be.
We should also take a look at alternative like https://github.com/jeelabs/esp-link#outbound-http-rest-requests-and-mqtt-client (with https://github.com/jeelabs/el-client client) or https://github.com/tuanpmt/espduino and see how the protocol/usage compare to it. Their protocol is apparently based on SLIP. |
We should take a look at them. In general I would be against using the SLIP protocol though. I think human readable is really the right approach for us, ease of comprehension and debugging should be pretty high on our priority list. But that is my personal bias, this was the reason I selected redis for my personal projects. |
|
||
The event stream will continue until you send CANCEL_STREAM. | ||
|
||
When there is an ongoing event stream no other commands can be processed until you call END_STREAM as the event stream owns the return line. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this note in here as well. I don't see a clean way around this, let me know if you disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need different command for CANCEL_STREAM
and END_STREAM
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: "no other commands can be processed", since our protocol is serial based the client will have to poll if Serial.available() Serial.readline()
for stream result, maybe it wouldn't hurt to make that more explicit with a command? that way this could be multiplexed with other operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed CANCEL_STREAM to END_STREAM, I think they should be the same.
I am not certain what you mean by the second comment. What would you like to make explicit? Do you want to cache the stream on the modem and have the client call "NEXT_EVENT"? We may be able to do that but memory could be an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't memory already an issue if we print something to Serial and nobody is reading on the other side?
We could have it read from the wifi connection only when we receive the NEXT_EVENT
command?
I'm not sure which one is better, I was just thinking out loud.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once the serial buffer fills up I think the serial write will just block until it is read. Also if the client can't do anything else with the connection they are less likely to leave it blocked than if they can just start a stream then ignore it while they do other things.
If we leave it in the wifi queue we are back to the state of not being able to do other actions while streaming. All other commands would have to return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said we can address the issue directly and measure memory usage, if it exceeds a certain amount shutdown the stream and return an error when they call "NEXT_EVENT".
However if we can stream directly to the serial line than I think we can handle much larger/faster streams more efficiently. Maybe are back to adding more modes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we leave it in the wifi queue we are back to the state of not being able to do other actions while streaming. All other commands would have to return an error.
So practically, it would have to be a different "connection", since the stream endpoint is different from the API endpoint (remember the 2x redirect you can after calling /stream
)
|
||
Every time a serial connection is established we will expect a “BEGIN” call after which any subsequent calls can be made, until the connection is closed. | ||
|
||
In the following examples we use $ to represent variables. For example $Host represents your firebase host. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: since $
is already used in the protocol, maybe we should using a different delimeter for placeholder like %VALUE%
, [VALUE]
or <VALUE>
LGTM just some nits. |
Add serial protocol for Firebase "modem"
Related #35.