Switch WiFi mode from AP to STA - c++

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());
}

Related

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

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);
}
};

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.

Player name on auto pick option for invitation

We have developed a multiplayer game using google play services.
When we send a request to the api for inviting friends to play, on clicking auto pick, the opponent's name does not show, instead the list shows any random name as in Player_1231,Player_3333 etc.
We need help regarding this issue. We need proper player names to play the game.Kindly check the screenshots attached.
Immediate help will be appreciated.
Please find the code below:
public void onRoomConnected(int statusCode, Room room) {
// TODO Auto-generated method stub
if (statusCode != mGamesClient.STATUS_PARTICIPANT_NOT_CONNECTED) {
// Toast.makeText(this, " is PARTICIPANT_CONNECTED.",
// Toast.LENGTH_SHORT).show();
roomId = room.getRoomId();
room_creator_id = room.getCreatorId();
// participantId = p.getParticipantId();
current_player_id = room.getParticipantId(mGamesClient
.getCurrentPlayerId());
Asset.self = Asset.username;
if (room_creator_id != null) {
if (room_creator_id.equals(current_player_id)) {
Server = true;
}
}
// Toast.makeText(this,
// " is PARTICIPANT_CONNECTED."+room_creator_id,
// Toast.LENGTH_SHORT).show();
par = null;
par = room.getParticipants();
for (Participant p : par) {
if (!p.getParticipantId().equals(current_player_id)) {
System.out.println(current_player_id
+ " After 1 connect " + p.getParticipantId());
participantId = p.getParticipantId();
Asset.opponent = p.getDisplayName();
break;
}
}
menu.initPage(GameConst.SELECTLEVEL_PAGE_ONLINE);
menu.Start_Selection_Timer();
}
// Toast.makeText(this, " is onRoomConnected.",
// Toast.LENGTH_SHORT).show();
}
PLAY ONLINE---------------
public void startQuickGame() {
// automatch criteria to invite 1 random automatch opponent.
// You can also specify more opponents (up to 3).
if (mGamesClient.isConnected()) {
Bundle am = RoomConfig.createAutoMatchCriteria(1, 1, 0);
// build the room config:
RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
roomConfigBuilder.setAutoMatchCriteria(am);
RoomConfig roomConfig = roomConfigBuilder.build();
// create room:
mGamesClient.createRoom(roomConfig);
} else {
Toast.makeText(con, "Wait for connection or try after some time",
Toast.LENGTH_SHORT).show();
mGamesClient.connect();
}
// go to game screen
}

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.

TTS over web-service in compressed format

I have developed TTS engine in .NET. Now I want to expose it over web.
I have used the base64 string encoding to transfer the WAV format, but it is slow when I pass longer text.
Now I'm considering to build some MP3 streaming (maybe with NAudio) where I will convert the WAV formated MemoryStream into MP3 stream and pass it to the client. Does anyone has some experience with this?
Does anyone has experience how to convert WAV MemoryStream with NAudio to MP3 MemoryStream?
public class MP3StreamingPanel2 : UserControl
{
enum StreamingPlaybackState
{
Stopped,
Playing,
Buffering,
Paused
}
private BufferedWaveProvider bufferedWaveProvider;
private IWavePlayer waveOut;
private volatile StreamingPlaybackState playbackState;
private volatile bool fullyDownloaded;
private HttpWebRequest webRequest;
public void StreamMP32(string url)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
SettingsSection section = (SettingsSection)config.GetSection("system.net/settings");
section.HttpWebRequest.UseUnsafeHeaderParsing = true;
config.Save();
this.fullyDownloaded = false;
webRequest = (HttpWebRequest)WebRequest.Create(url);
int metaInt = 0; // blocksize of mp3 data
webRequest.Headers.Clear();
webRequest.Headers.Add("GET", "/ HTTP/1.0");
// needed to receive metadata informations
webRequest.Headers.Add("Icy-MetaData", "1");
webRequest.UserAgent = "WinampMPEG/5.09";
HttpWebResponse resp = null;
try
{
resp = (HttpWebResponse)webRequest.GetResponse();
}
catch (WebException e)
{
if (e.Status != WebExceptionStatus.RequestCanceled)
{
//ShowError(e.Message);
}
return;
}
byte[] buffer = new byte[16384 * 4]; // needs to be big enough to hold a decompressed frame
try
{
// read blocksize to find metadata block
metaInt = Convert.ToInt32(resp.GetResponseHeader("icy-metaint"));
}
catch
{
}
IMp3FrameDecompressor decompressor = null;
try
{
using (var responseStream = resp.GetResponseStream())
{
var readFullyStream = new ReadFullyStream(responseStream);
readFullyStream.metaInt = metaInt;
do
{
if (bufferedWaveProvider != null && bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4)
{
Debug.WriteLine("Buffer getting full, taking a break");
Thread.Sleep(500);
}
else
{
Mp3Frame frame = null;
try
{
frame = Mp3Frame.LoadFromStream(readFullyStream, true);
}
catch (EndOfStreamException)
{
this.fullyDownloaded = true;
// reached the end of the MP3 file / stream
break;
}
catch (WebException)
{
// probably we have aborted download from the GUI thread
break;
}
if (decompressor == null)
{
// don't think these details matter too much - just help ACM select the right codec
// however, the buffered provider doesn't know what sample rate it is working at
// until we have a frame
WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frame.FrameLength, frame.BitRate);
decompressor = new AcmMp3FrameDecompressor(waveFormat);
this.bufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat);
this.bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20); // allow us to get well ahead of ourselves
//this.bufferedWaveProvider.BufferedDuration = 250;
}
int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
//Debug.WriteLine(String.Format("Decompressed a frame {0}", decompressed));
bufferedWaveProvider.AddSamples(buffer, 0, decompressed);
}
} while (playbackState != StreamingPlaybackState.Stopped);
Debug.WriteLine("Exiting");
// was doing this in a finally block, but for some reason
// we are hanging on response stream .Dispose so never get there
decompressor.Dispose();
}
}
finally
{
if (decompressor != null)
{
decompressor.Dispose();
}
}
}
}
NAudio does not include an MP3 encoder. When I need to encode MP3 I use lame.exe. If you don't want to go via a file, lame.exe allows you to read from stdin and write to stdout, so if you redirect standard in and out on the process you can convert on the fly.