Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

initial json support #43

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 25 additions & 28 deletions Firebase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ Firebase& Firebase::auth(const String& auth) {
return *this;
}

String Firebase::get(const String& path) {
FirebaseObject Firebase::create() {
return FirebaseObject{};
}

FirebaseObject Firebase::get(const String& path) {
return sendRequest("GET", path);
}

String Firebase::push(const String& path, const String& value) {
return sendRequest("POST", path, value);
FirebaseObject Firebase::push(const String& path, const FirebaseObject& value) {
char buffer[256];
value.printTo(buffer, sizeof(buffer));
return sendRequest("POST", path, buffer);
}

Firebase& Firebase::stream(const String& path) {
_error.reset();
FirebaseObject Firebase::stream(const String& path) {
String url = makeURL(path);
const char* headers[] = {"Location"};
_http.setReuse(true);
Expand All @@ -55,11 +60,11 @@ Firebase& Firebase::stream(const String& path) {
statusCode = _http.sendRequest("GET", (uint8_t*)NULL, 0);
}
if (statusCode != 200) {
_error.set(statusCode,
"stream " + location + ": "
+ HTTPClient::errorToString(statusCode));
return FirebaseObject(FirebaseError(statusCode,
"stream " + location + ": "
+ HTTPClient::errorToString(statusCode)));
}
return *this;
return FirebaseObject{};
}

String Firebase::makeURL(const String& path) {
Expand All @@ -74,19 +79,17 @@ String Firebase::makeURL(const String& path) {
return url;
}

String Firebase::sendRequest(const char* method, const String& path, const String& value) {
_error.reset();
FirebaseObject Firebase::sendRequest(const char* method, const String& path, const String& value) {
String url = makeURL(path);
_http.begin(_host.c_str(), firebasePort, url.c_str(), true, firebaseFingerprint);
int statusCode = _http.sendRequest(method, (uint8_t*)value.c_str(), value.length());
if (statusCode < 0) {
_error.set(statusCode,
String(method) + " " + url + ": "
+ HTTPClient::errorToString(statusCode));
return "";
return FirebaseObject(FirebaseError(statusCode,
String(method) + " " + url + ": "
+ HTTPClient::errorToString(statusCode)));
}
// no _http.end() because of connection reuse.
return _http.getString();
return FirebaseObject(_http.getString());
}

bool Firebase::connected() {
Expand All @@ -97,18 +100,12 @@ bool Firebase::available() {
return _http.getStreamPtr()->available();
}

Firebase::Event Firebase::read(String& event) {
FirebaseObject Firebase::read() {
auto client = _http.getStreamPtr();
Event type;;
String typeStr = client->readStringUntil('\n').substring(7);
if (typeStr == "put") {
type = Firebase::Event::PUT;
} else if (typeStr == "patch") {
type = Firebase::Event::PATCH;
} else {
type = Firebase::Event::UNKNOWN;
}
event = client->readStringUntil('\n').substring(6);
String event = client->readStringUntil('\n').substring(7);
String data = client->readStringUntil('\n').substring(6);
client->readStringUntil('\n'); // consume separator
return type;
FirebaseObject result(data);
result["event"] = event;
return result;
}
71 changes: 44 additions & 27 deletions Firebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,50 +24,67 @@
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

// FirebaseError represents a Firebase API error with a code and a
// message.
class FirebaseError {
// FirebaseError is an error for a given firebase API call.
struct FirebaseError {
FirebaseError() {}
FirebaseError(int c, const String& m) : code(c), message(m) {}
FirebaseError(int c, const char* m) : code(c), message(m) {}
int code = 0;
String message = "";
operator bool() const {
return code < 0;
}
};

// FirebaseObject is a payload or a result for a given firebase API call.
class FirebaseObject {
public:
operator bool() const { return _code < 0; }
int code() const { return _code; }
const String& message() const { return _message; }
void reset() { set(0, ""); }
void set(int code, const String& message) {
_code = code;
_message = message;
FirebaseObject() : _json(_buf.createObject()) {
}
FirebaseObject(const FirebaseObject& obj) : FirebaseObject(obj._data) {
}
FirebaseObject(const String& data) : _data(data), _json(_buf.parseObject((char*)_data.c_str())) {
if (!_json.success()) {
_error = FirebaseError{-1, "error parsing json"};
}
}
FirebaseObject(const FirebaseError& err) : _error{err}, _json(_buf.createObject()) {
}
const FirebaseError& error() { return _error; }
JsonObjectSubscript<const char*> operator[](const char* key) {
return _json[key];
}
size_t printTo(char *buffer, size_t bufferSize) const {
return _json.printTo(buffer, bufferSize);
}
private:
int _code = 0;
String _message = "";
FirebaseError _error;
String _data;
StaticJsonBuffer<200> _buf;
JsonObject& _json;
};

// Firebase is the connection to firebase.
// Firebase is a client for a given firebase host.
class Firebase {
public:
Firebase(const String& host);
Firebase& auth(const String& auth);
const FirebaseError& error() const {
return _error;
}
String get(const String& path);
String push(const String& path, const String& value);
FirebaseObject get(const String& path);
FirebaseObject push(const String& path, const FirebaseObject& value);
bool connected();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel like the concept of being connected or available is only relevant to one usecase (the stream call) so it seems like this should all be separated from the Firebase object to keep it as simple as possible to understand for the other uses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for available() because the read() call is separate, but it could also be useful for a non blocking version.

connected is useful if we implement a separate warm up connect, to avoid paying an extra latency tax on the first api call.

Firebase& stream(const String& path);
FirebaseObject stream(const String& path);
bool available();
enum Event {
UNKNOWN,
PUT,
PATCH
};
Event read(String& event);
FirebaseObject read();
FirebaseObject create();
private:
String makeURL(const String& path);
String sendRequest(const char* method, const String& path, const String& value = "");
FirebaseObject sendRequest(const char* method, const String& path, const String& value = "");
HTTPClient _http;
String _host;
String _auth;
FirebaseError _error;
};


#endif // firebase_h
20 changes: 11 additions & 9 deletions examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <Firebase.h>

// create firebase client.
Firebase fbase = Firebase("example.firebaseio.com")
Firebase fbase = Firebase("firebase-arduino-example.firebaseio-demo.com")
.auth("secret_or_token");

void setup() {
Expand All @@ -38,17 +38,19 @@ void setup() {
Serial.println(WiFi.localIP());

// add a new entry.
String l = fbase.push("/logs", "{\".sv\": \"timestamp\"}");
FirebaseObject obj = fbase.create();
obj[".sv"] = "timestamp";
FirebaseObject result = fbase.push("/logs", obj);

// handle error.
if (fbase.error()) {
Serial.println("Firebase request failed");
Serial.println(fbase.error().message());
if (result.error()) {
Serial.print("firebase request failed: ");
Serial.println(result.error());
return;
}
// print response.
Serial.println(l);
// print all entries.
Serial.println(fbase.get("/logs"));

// print result.
Serial.println(result["name"]);
}

void loop() {
Expand Down
41 changes: 22 additions & 19 deletions examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,29 @@ void setup() {


void loop() {
if (fbase.error()) {
Serial.println("streaming error");
Serial.println(fbase.error().message());
}
if (fbase.available()) {
String event;
auto type = fbase.read(event);
Serial.print("event: ");
Serial.println(type);
if (type != Firebase::Event::UNKNOWN) {
FirebaseObject result = fbase.read();
if (result.error) {
Serial.println("firebase streaming error");
Serial.println(result.error);
return;
}
if (result.json["event"] == "put") {
String path = result.json["path"];
float data = result.json["data"];
Serial.print("path: ");
Serial.println(path);
Serial.print("data: ");
Serial.println(event);

// TODO(proppy): parse JSON object.
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(event);
display.display();
Serial.println(data);
if (path != "/_updated") {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println(path.c_str()+1);
display.println(data);
display.display();
}
}
}
}
}