Firebase C++ SDK Desktop how to get connection status? - c++

I am using Firebase C++ SDK on Windows, is there a way I can get connection status to firebase?
I have checked ".info/connected" and listen to its changed in value, but it does not work correctly, it gives status only when I get connected to Internet again, and only works one time, after that it just does not listen for changes. Here is the code i am using:
class ConnectionValueListener : public ValueListener
{
public:
void OnValueChanged(const firebase::database::DataSnapshot& snapshot) override
{
bool status = snapshot.value().AsBool().bool_value();
if (!status) {
return;
}
auto res = databaseRef.OnDisconnect()->SetValue(isOfflineForDatabase);
if (res.error() == firebase::database::kErrorNone) {
res = databaseRef.OnDisconnect()->SetValue(isOnlineForDatabase);
return;
}
}
void OnCancelled(const firebase::database::Error& error_code, const char* error_message) override
{
LogMessage("ValueListener canceled: " + std::to_string(error_code) + error_message);
}
};

Related

Switch WiFi mode from AP to STA

I'm working on project with ESP32-S2-Saola-1M board in C/C++ via ESP-IDF framework. At the beginning I initialize a Wi-Fi in an AP mode and starts the HTTP WebServer to get WiFi data from user through browser. After the user saves his Wi-Fi data (SSID and Passwd) throught page, the
HTTP server should shut down and Wi-Fi switch from AP mode to STA mode - connect to user's Wi-Fi. I have problem with this part. I don't know how to solve this elegantly and in principle correctly. So can someone describe me any solutions or better ideas please?
I thought of using method with while cycle and POST handler. After the data comes from page via POST request, handler saves them and set some bool property (e.g. hasData in code below) to true and while cycle in method breaks/stops and other code in application can continue. Something like semaphore. Simply:
start Wi-Fi (AP mode)
start webserver
wait until user sends his Wi-Fi data
stop webserver
stop Wi-Fi AP mode -> switch to STA mode
next actions... (measure, send data out, deep sleep etc.)
Pseudo code:
bool hasData = false;
static esp_err_t postHandler(httpd_req_t *request)
{
.
. //saves data from POST request
.
hasData = true;
return ESP_OK;
}
void waitForUser(void)
{
while(hasData != true) {
//just wait
}
}
static const httpd_uri_t postData = {
.uri = "/connection",
.method = HTTP_POST,
.handler = postHandler,
.user_ctx = NULL
};
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
if (httpd_start(&server, &config) == ESP_OK) {
httpd_register_uri_handler(server, &postData);
return server;
}
return NULL;
}
void stop_webserver(httpd_handle_t server)
{
if (server) {
httpd_stop(server);
}
}
void wifiAPInit(void)
{
.
. //Initialize wifi config for AP mode and start wifi
.
}
void app_main(void)
{
.
. //Initialize NVS and others...
.
httpd_handle_t server = NULL;
wifiAPinit();
server = start_webserver();
waitForUser();
stop_webserver(server);
.
. // start Wifi in STA mode and continue...
.
}
Is this principle correct?
Thanks for advice!
By far the easiest approach is to just reboot (esp_restart()) after saving new configuration.
Then select the right mode after reading the configuration on boot.
void app_main()
{
// init NVS
// load configuration from NVS
if (config.wifiSSID.empty()) {
startAPmode();
} else {
startSTAmode(config.wifiSSID, config.wifiPassword);
}
start_webserver();
// etc ...
}
You can use Both Sta and Ap at the same time
void connectSTA(char *ssid, char *pass)
{
wifi_config_t wifi_config;
memset(&wifi_config, 0, sizeof(wifi_config));
strcpy((char *)wifi_config.sta.ssid, said);
strcpy((char *)wifi_config.sta.password, pass);
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
}
void connectAP()
{
wifi_config_t wifi_config =
{
.ap = {
.ssid = "my esp32",
.password = "P#ssword",
.max_connection = 4,
.authmode = WIFI_AUTH_WPA_WPA2_PSK}};
esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config);
}
void wifiInit(void *params)
{
ESP_ERROR_CHECK(nvs_flash_init());
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
nvs_handle_t nvs;
nvs_open("wifiCreds", NVS_READWRITE, &nvs);
size_t ssidLen, passLen;
char *ssid = NULL, *pass = NULL;
if (nvs_get_str(nvs, "ssid", NULL, &ssidLen) == ESP_OK)
{
if (ssidLen > 0)
{
ssid = malloc(ssidLen);
nvs_get_str(nvs, "ssid", ssid, &ssidLen);
}
}
if (nvs_get_str(nvs, "pass", NULL, &passLen) == ESP_OK)
{
if (passLen > 0)
{
pass = malloc(passLen);
nvs_get_str(nvs, "pass", pass, &passLen);
}
}
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
connectAP();
if (ssid != NULL && pass != NULL)
{
connectSTA(ssid, pass);
}
ESP_ERROR_CHECK(esp_wifi_start());
}

Calling a gRPC call after one returns from the server side

I have these two gRPC calls:
service Console {
rpc JoinNetwork(keyvaluestore.Request) returns (ClusterInfo) {}
rpc WatchHealth(HealthCheckRequest) returns (stream HealthCheckResponse) {}
}
Which are used by two entities: a masternode and a storage node. When a storage node first starts up, it calls JoinNetwork() to the masternode, which returns some information to the storage node.
I then want to call WatchHealth() from the masternode, which pings the storage node, which in returns continually sends a "heartbeat" message in perpetuity to the server until it crashes.
The only way I can think of chaining these two calls together is calling WatchHealth inside the server implementation of JoinNetwork. But I really only want to call WatchHealth after JoinNetwork returns, which I'm not sure how to do.
JoinNetwork (client):
bool ConsoleClient::JoinNetwork(const string& my_addr, ClusterInfo* cinfo) {
ClientContext ctx;
Request req;
Status status = stub_->JoinNetwork(&ctx, req, cinfo);
if (status.ok()) {
log("[C: JoinNetwork] Successfully joined cluster " + to_string(cinfo->cid()), VB);
return true;
} else {
log("[C: JoinNetwork] Error " + to_string(status.error_code()) + ": " + status.error_message(), VB);
return false;
}
}
JoinNetwork (server):
Status ConsoleServiceImpl::JoinNetwork(ServerContext* ctx, const Request* req, ClusterInfo* res) {
MLOCK.lock();
int cid = (NUM_NODES++) % K;
CDIR[cid].insert(req->addr());
if (CPRIMES.find(cid) == CPRIMES.end()) {
CPRIMES.insert({ cid, req->addr() }); // If no primary exists, make it the primary
}
res->set_cid(cid);
res->set_sid(assign_sid(cid));
res->set_caddrs(pack_addrs(CDIR[cid]));
res->set_primary(CPRIMES[cid]);
log("[S: JoinNetwork] Storage node " + req->addr() + " has joined cluster " + to_string(cid), VB);
MLOCK.unlock();
return Status::OK;
}
WatchHealth (client):
bool ConsoleClient::WatchHealth(const string& addr) {
ClientContext ctx;
auto deadline = chrono::system_clock::now() + chrono::milliseconds(500);
ctx.set_deadline(deadline);
HealthCheckRequest req;
HealthCheckResponse res;
unique_ptr<ClientReader<HealthCheckResponse>> reader(stub_->WatchHealth(&ctx, req));
while (reader->Read(&res));
Status status = reader->Finish();
if (status.ok()) {
log("[C: WatchHealth] Storage node " + addr + " shutdown gracefully", VB);
return true;
} else {
log("[C: WatchHealth] Storage node " + addr + " crashed", VB);
return false;
}
}
WatchHealth (server):
Status ConsoleServiceImpl::WatchHealth(ServerContext* ctx, const HealthCheckRequest* req, ServerWriter<HealthCheckResponse>* writer) {
HealthCheckResponse res;
while (true) {
res.set_status(console::HealthCheckResponse::SERVING);
writer->Write(res);
}
res.set_status(console::HealthCheckResponse::SHUTTING_DOWN);
writer->Write(res);
return Status::OK;
}

How to get the state of a service with sd-bus?

I need to query, monitor and possibly change the state of a few systemd services from a C++ application. It looks like sd-bus is the right way to do this, but I'm having a terrible time finding an example.
So, how do I:
1) Query the current status of a service via sd-bus, similar to systemctl status foo.service?
2) Monitor the status of a service such that I get a callback whenever it changes?
3) Change the status of a service, similar to systemctl start/stop/restart?
Thanks!
Using the sd-bus API is absolutely correct (header #include <systemd/sd-bus.h>)
First you need get access to a bus object:
I do this:
Systemctl::Systemctl() :
m_bus(nullptr)
{
int r = sd_bus_default_system(&m_bus);
if (r < 0)
throw exception("Could not open systemd bus");
}
If you're having problems opening the bus:
Run as root/sudo
Make some polkit policies to grant your user/group access to this command
Run the _user bus instead of the _system bus
Don't forget to release the bus when you are done:
Systemctl::~Systemctl()
{
sd_bus_unref(m_bus);
}
Now you had 3 questions:
Query the status
For each unit, I have a class which holds the escaped name (foo_2eservice) as m_name, and a reference to the bus in m_bus. Call this method with any property. You seem to be most interested in "ActiveState" or "SubState".
std::string Unit::GetPropertyString(const std::string& property) const
{
sd_bus_error err = SD_BUS_ERROR_NULL;
char* msg = nullptr;
int r;
r = sd_bus_get_property_string(m_bus,
"org.freedesktop.systemd1",
("/org/freedesktop/systemd1/unit/" + m_unit).c_str(),
"org.freedesktop.systemd1.Unit",
property.c_str(),
&err,
&msg);
if (r < 0)
{
std::string err_msg(err.message);
sd_bus_error_free(&err);
std::string err_str("Failed to get " + property + " for service "
+ m_name + ". Error: " + err_msg);
throw exception(err_str);
}
sd_bus_error_free(&err);
// Free memory (avoid leaking)
std::string ret(msg);
free (msg);
return ret;
}
Monitor the status of a service:
The first step is to set up a file-descriptor to subscribe to changes. In this case you are interested in subscribing to the "PropertiesChanged" signal. Note that you'll get a signal for any property changing, not just the state. In the sd_bus_add_match() call, there is room for a callback, though I haven't experimented with it.
void Systemctl::SubscribeToUnitChanges(const std::string& escaped_name)
{
/* This function is an easier helper, but it as only introduced in systemd 237
* Stretch is on 232 while buster is on 241 . Need re replace this as long as
* we still support stretch
sd_bus_match_signal(
m_bus,
nullptr, // slot
nullptr, // sender
std::string("/org/freedesktop/systemd1/unit/" + escaped_name).c_str(), // path
"org.freedesktop.DBus.Properties", // interface
"PropertiesChanged", // member
nullptr, // callback
nullptr // userdata
);
*/
std::string match = "type='signal'";
match += ",path='/org/freedesktop/systemd1/unit/" + escaped_name + "'" ;
match += ",interface='org.freedesktop.DBus.Properties'";
match += ",member='PropertiesChanged'";
sd_bus_add_match(
m_bus,
nullptr, // slot
match.c_str(),
nullptr, // callback
nullptr // userdata
);
}
Instead what I do is periodically poll the bus for the subscribed changes and update each unit:
bool Systemctl::ProcessBusChanges()
{
bool changed = false;
sd_bus_message* msg = nullptr;
// for each new message
std::list<std::string> escaped_names;
while( sd_bus_process(m_bus, &msg) )
{
// Note: Once sd_bus_process returns 0, We are supposed to call
// sd_bus_wait, or check for changes on sd_bus_get_fd before calling
// this function again. We're breaking that rule. I don't really know
// the consequences.
if (msg)
{
std::string path = strna( sd_bus_message_get_path(msg) );
sd_bus_message_unref(msg);
std::string escaped_name = path.erase(0, path.find_last_of('/')+1 );
escaped_names.push_back(escaped_name);
changed = true;
}
}
escaped_names.sort();
escaped_names.unique();
for (auto unit : escaped_names)
{
auto it = m_units.find(unit);
if (it != m_units.end())
it->second.RefreshDynamicProperties();
}
return changed;
}
If it tells us that the bus has changed, then I go ahead and read all of my monitored units on that bus.
Change the status
This one is easy. I use the following, where method is one of "StartUnit", "StopUnit", or "RestartUnit".
static void CallMethodSS(sd_bus* bus,
const std::string& name,
const std::string& method)
{
sd_bus_error err = SD_BUS_ERROR_NULL;
sd_bus_message* msg = nullptr;
int r;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1", /* <service> */
"/org/freedesktop/systemd1", /* <path> */
"org.freedesktop.systemd1.Manager", /* <interface> */
method.c_str(), /* <method> */
&err, /* object to return error in */
&msg, /* return message on success */
"ss", /* <input_signature (string-string)> */
name.c_str(), "replace" ); /* <arguments...> */
if (r < 0)
{
std::string err_str("Could not send " + method +
" command to systemd for service: " + name +
". Error: " + err.message );
sd_bus_error_free(&err);
sd_bus_message_unref(msg);
throw exception(err_str);
}
// Extra stuff that might be useful: display the response...
char* response;
r = sd_bus_message_read(msg, "o", &response);
if (r < 0)
{
LogError("Failed to parse response message: %s\n", strerror(-r) );
}
sd_bus_error_free(&err);
sd_bus_message_unref(msg);
}

Trying to understand a parsing error when making an HTTP request with C++ on ESP32

I'm attempting to do an HTTPS PUT from C++ on an ESP-EYE. I started with the C esp_https_example code and had that working with the same PEM and URL. I started to transition to using this from C++ as part of a project that is primarily written in C++. My call looks like:
static const char *URL = "https://signal.unexpectedeof.casa/on-air";
void https_with_url(void)
{
esp_http_client_config_t* config = (esp_http_client_config_t*)calloc(sizeof(esp_http_client_config_t), 1);
config->url = URL;
config->cert_pem = unexpectedeof_casa_root_cert_pem_start;
config->event_handler = _http_event_handler;
esp_http_client_handle_t client = esp_http_client_init(config);
esp_http_client_set_method(client, HTTP_METHOD_PUT);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
I believe the URL I'm providing isn't being copied or initialized correctly resulting in a url parsing error. When the function https_with_url is called I get this error:
E (13593) esp-tls: couldn't get hostname for :signal.unexpectedeof.casa:
E (13593) esp-tls: Failed to open new connection
E (13603) TRANS_SSL: Failed to open a new connection
E (13603) HTTP_CLIENT: Connection failed, sock < 0
E (13613) HTTPS_HANDLING: Error perform http request ESP_ERR_HTTP_CONNECT
I (13623) HTTPS_HANDLING: HTTP_EVENT_DISCONNECTED
I (13623) HTTPS_HANDLING: HTTP_EVENT_DISCONNECTED
Since I'm using C++ but the parse happens in the esp-idf C code I thought maybe I'm not passing the data correctly, but haven't made much progress. Switching from an inline string for the URL to the character array shown didn't make a difference.
esp-idf version 4.1.
Ended up being that I didn't handle the wifi event properly before the request was sent.
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
s_retry_num++;
ESP_LOGI(LOGTAG, "retry to connect to the AP");
}
ESP_LOGI(LOGTAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(LOGTAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL));
wifi_config_t wifi_config = { };
strcpy((char*)wifi_config.sta.ssid, CONFIG_ESP_WIFI_SSID);
strcpy((char*)wifi_config.sta.password, CONFIG_ESP_WIFI_PASSWORD);
ESP_LOGI(LOGTAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(LOGTAG, "wifi_init_sta finished.");
}
Examples of this are in the esp-idf github repo under examples/protocols/wifi.

Getting org.xmlpull.v1.XmlPullParserException for SOAP connection in BlackBerry

I am using ksoap2.jar in my application for webservice call.
While conneting, to the server it is giving the below error when i am testing using Wi-fi or simulator:
org.xmlpull.v1.XmlPullParserException: unexpected type (position:END_DOCUMENT null#1:0 in java.io.InputStreamReader#6111a1f1)
And when i am testing using SIM Card, it is throwing this error: java.io.InterruptedIOException: Local connection timed out after ~ 120000
I have written the below code for this webservice connection:
SoapObject rpc = new SoapObject(serviceNamespace, methodName);
rpc.addProperty("UserID", "1");
rpc.addProperty("UserName", "xyz");
rpc.addProperty("ContactNo", "9014567890");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = rpc;
envelope.dotNet = true;
envelope.encodingStyle = SoapSerializationEnvelope.ENC;
HttpTransport ht = new HttpTransport(serviceUrl + getConnectionString().trim());
ht.debug = true;
System.out.println("debug true ------- ");
ht.setXmlVersionTag(" version=\"1.0\" encoding=\"UTF-8\"?>");
SoapObject response = null;
try
{
ht.call(soapAction, envelope);
response = (SoapObject )envelope.getResponse();
String resultbody = response.getProperty("updateUserDetailResult").toString();
}
catch(org.xmlpull.v1.XmlPullParserException ex2){
resultLbl.setText(ex2.toString());
}
catch(Exception ex){
String bah = ex.toString();
resultLbl.setText(bah);
}
/**
* Looks through the phone's service book for a carrier provided BIBS network
*
* #return The uid used to connect to that network.
*/
private static String getCarrierBIBSUid()
{
net.rim.device.api.servicebook.ServiceRecord[] records = ServiceBook.getSB().getRecords();
int currentRecord;
for (currentRecord = 0; currentRecord < records.length; currentRecord++)
{
if (records[currentRecord].getCid().toLowerCase().equals("ippp")) {
if (records[currentRecord].getName().toLowerCase().indexOf("bibs") >= 0) {
return records[currentRecord].getUid();
}
}
}
return null;
}
/**
* Getting the connection string based on the connection
* #return
*/
public static String getConnectionString()
{
String connectionString = null;
// Wifi is the preferred transmission method
if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED)
{
connectionString = ";interface=wifi";
}
// Is the carrier network the only way to connect?
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT)
{
String carrierUid = getCarrierBIBSUid();
if (carrierUid == null)
{
// Has carrier coverage, but not BIBS. So use the carrier's TCP
// network
connectionString = ";deviceside=true";
}
else
{
// otherwise, use the Uid to construct a valid carrier BIBS
// request
connectionString = ";deviceside=false;connectionUID=" + carrierUid + ";ConnectionType=";
}
}
// Check for an MDS connection instead (BlackBerry Enterprise Server)
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS)
{
connectionString = ";deviceside=false";
}
// If there is no connection available abort to avoid bugging the user
// unnecssarily.
else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE)
{
}
// In theory, all bases are covered so this shouldn't be reachable.
else
{
connectionString = ";deviceside=true";
}
return connectionString;
}
I am using the preverified ksoap jar file(ksoap2-j2me-core-prev-2.1.2.jar).
I am not getting exactly where it is going wrong. I have searched in the forum but did not get the proper solution.
Thanks.