Facing issue with file download using ESP32 webserver - arduino

I am developing code for ESP32 (16MB) using PlatformIO with Arduino framework, On ESP32, I am using both Bluetooth as well as Wi-Fi connectivity. When Mobile App is connected with Bluetooth of ESP32 data will be sent on mobile using Bluetooth on BLE characteristics (working perfectly fine), and when Bluetooth is not connected data will be logged in .json file (have used SPIFFS library for this and file creation is working fine). When user wants to have log data, ESP32 will create Wi-Fi Access Point (AP) (used WiFi.h) and by connecting to that AP user should be able to download .json file by accessing link "192.168.4.1/download" on browser. Once the file will be downloaded on Mobile App user will access link "192.168.4.1/finisheddownload" and file will be deleted from ESP32.
after successfully connecting to AP of ESP32, while I access link "192.168.4.1/download" from browser, the code gets stuck at that point or sometimes it reboots the ESP32 and performs nothing and if I access "192.168.4.1/finisheddownload", it successfully deletes the file. My file size is around 10MB but even I perform this action when file size is of 1KB it behaves same. I have tested the same code on ESP32 (4MB) module and was able to download file for around 50KB.
I am not sure whether it is the issue of memory or library which I am using or limitation of ESP32. I am attaching code snippet along with memory partition I have used, please guide.
Error when ESP32 gets reboot after accessing link for file download:
abort() was called at PC 0x401d844b on core 1
Backtrace: 0x40092b60:0x3ffcf8e0 0x40092d91:0x3ffcf900 0x401d844b:0x3ffcf920 0x401d8492:0x3ffcf940 0x401bf359:0x3ffcf960 0x401bf0dc:0x3ffcf980 0x400fa67e:0x3ffcf9a0 0x400fa75a:0x3ffcf9d0 0x400e62b9:0x3ffcf9f0 0x400d23e4:0x3ffcfa20 0x400d27e3:0x3ffcfa40 0x400d2ed7:0x3ffcfa70 0x400d35f1:0x3ffcfa90 0x400e9151:0x3ffcfb10 0x4008f275:0x3ffcfb30
Rebooting...
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5828
entry 0x400806ac
Code snippet:
void downloadfile(){ //function to download file on mobile App
delay(500);
do{
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->stop();
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
WiFi.mode(WIFI_AP);
delay(100);
Serial.print("Setting soft-AP ... ");
Serial.println(WiFi.softAP("ESP32","123456789") ? "Ready" : "Failed!");
Serial.print("Soft-AP IP address = ");
Serial.println(WiFi.softAPIP());
delay(100);
// send a file when /download is requested
server.on("/download", HTTP_ANY, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/file1.json", "text/plain", true);
});
server.on("/finisheddownload", HTTP_GET, [](AsyncWebServerRequest *request){
logfile.del_file();
request->send(200, "text/html", "Download complete response");
delay(500);
Actual_filexist = 0;
WiFi.softAPdisconnect(true);
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
delay(500);
});
server.begin();
delay(500);
}while(Actual_filexist == 1);
}
memory partition:
#name Type Subtype Offset Size Flags
nvs data nvs 0x9000 0x7000
app0 app ota_0 0x10000 0x2A0000
eeprom data 0x99 0x2F0000 0x1A8CE0
spiffs data spiffs 0x498CE0 0xA7D8C0
Memory usage:
RAM: [== ] 19.9% (used 65292 bytes from 327680 bytes)
Flash: [====== ] 60.1% (used 1654361 bytes from 2752512 bytes)
Thank you....

You're using ESPAsyncWebServer. Its documentation explicitly states that you should not call delay() or yield() or any function that calls them in callbacks from the web server (Important things to remember. You're calling delay() twice in the /finisheddownload handler.
You're also calling WiFi.softAPdisconnect() in the handler before the handler can return, which means you're turning off Wifi before the callback returns and potentially before the response is sent to the browser. It's not safe to do this in the callback.
You need to do as little as possible in the callback from the web server. Rewrite that handler to set a flag which loop() will inspect so that all of the work it does except for the call to request->send() is actually done in loop().

Related

ESP-IDF's equivalent of Arduino's WiFiClient.write() fucntionality

I'm using ESP32-CAM for video streaming using ESP-IDF framework. I was able to create server on ESP32-CAM and stream video sucessfully. For streaming I'm using HTTP protocol with Content-Type: multipart/x-mixed-replace; boundary=" PART_BOUNDARY "\r\n headers and it's working great :)
Now, I want to add a servo motor to the camera to adjust pan remotely. I think of 2 ways to do it.
Create another endpoint for servo, but for that I need to disconnect video streaming untill servo request is completed. This gives significant lag in video.
send data in between video streaming connection and get minimum lag in video.
I was able to use 2nd option in Arduino IDE for WebServer, there we have an option of reading binary data from client in an ongoing request. Example below
// only showing the relevent code ...
// create server.
WebServer server(80);
// then register different endpoint handlers ...
// ...
// ...
void video_stream_handler(){
// initilize camera stuff, nothing to worry here
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char buf[32];
sensor_t* sensor_settings = esp_camera_sensor_get();
sensor_settings->set_framesize(sensor_settings, FRAMESIZE_VGA);
sensor_settings->set_quality(sensor_settings, 20);
// get client handle
WiFiClient client = server.client();
// now we can write headers as well as data to client. this works in ESP-IDF as well :)
client.write(<some-headers>, <header-length>);
// now this is interesting.
// we can read from client as well
client.read(); // gives bytes read from client
My question is, is it possible with esp-idf to do something like this?
Or if there is any other better alternative than all of it? I want minimum lag in video streaming while still performing servo action in between.
Hardware: ESP32-CAM(single core) with 4MB PSRAM
ESP-IDF http_server reference
P.S. I'm using Python socket to read/process the video stream and to send binary data in ongoing connection.

Arduino ESP32 receive file over BLE (for OTA update)

I want to send a file (bin) from my Xamarin Forms (C#) iOS App over BLE (https://github.com/xabre/xamarin-bluetooth-le) to my ESP32 (Arduino). That file would be a bin file for updating. I found already a solution on how to update the ESP32 out of spiffs (arduino-esp32 do OTA via BLE) but does anyone know how I can receive the file with the ESP32 and save it into spiffs?
(The BLE connection app to esp32 is already fully working, I can send texts, but I don't know how to send files)
Best regards
nflug
You have to send the file content as individual bytes and save it to SPIFFS
You can create and write to a binary file using the function below (Arduino code)
void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {
//Serial.printf("Write binary file %s\r\n", path);
File file = fs.open(path, FILE_APPEND);
if (!file) {
Serial.println("- failed to open file for writing");
return;
}
file.write(dat, len);
file.close();
}
Check out the usage here ESP32_BLE_OTA_Arduino.
I have also created an android app to upload the file ESP32_BLE_OTA_Android

WebServer + AP not working? WebServer + STA does

I've been trying to set up an access point combined with a web server using the ESP8266WiFi libraries for Arduino. I am using an ESP8266 Huzzah module with NodeMCU firmware installed (newest master).
Setting up an AP using softAP works fine, I can also assert a PSK to it and choose which channel to use (I've chosen the one with the least traffic, in this case ch 11). The problem is when trying to access the web server from a connected device, it always ends up with a timeout exception. The ESP's IP is in this case 192.168.4.1 and the port is 80.
I've tried connecting the ESP to another AP (Home router) in STA mode and I am successfully able to connect to the ESP through the given IP Address.
To sum it all up:
ESP8266WebServer + WIFI_AP (Station) => Does not work!
ESP8266WebServer + WIFI_STA (Client) => Works!
This is how I set the web server up, together with AP:
In Setup:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
[...]
ESP8266WebServer server(80);
[...]
Serial.print("Setting soft-AP ... ");
WiFi.disconnect(true);
WiFi.mode(WIFI_AP);
delay(100);
boolean result = WiFi.softAP("TestAP", "0123456789", 11); // This does of course return true, and the access point shows up on my device
if(result == true)
{
Serial.println("Access Point Ready");
Serial.println(WiFi.softAPIP()); // Prints 192.168.4.1
}
else
{
Serial.println("Access Point Failed!");
}
[...]
server.on("/", [](){
server.send(200, "text/html", webPage);
delay(1000);
});
[...]
server.begin();
In Loop:
server.handleClient();
Has anyone else done this before and had it work? What am I actually doing wrong here?
Ok, so it seems that I figured it out partly...
All the time, I was using my smartphone to connect to the ESP AP and trying to access the web server. I tried connecting from my computer and by all the stupidness in this world, it works.
Why I can not connect via the phone is a mystery to me, maybe someone can tell me what is going on here...
Basically on certain android devices look out for a notification about staying connected as mentioned in the comments.
If you don't accept staying connected your phone will still show as "connected with out internet" and the page will not load, once you have accepted the notification about staying connected then you can load your wemos page.
On an android nvidia based tablet this was not required, but on my motorola smart phone it would not work until i accepted that box.

Store WIFI Credentials

When I call WiFi.begin(ssid, pass), it saves your credentials. So next time your sketch runs, it will connect automatically.
But if I call WiFi.config(IP, Gate, Subnet) before that, it connects without using the DHCP server.
But after restart it's using DHCP again.
It seems to me that the WiFi.config parameters are not stored anywhere for further usage. Am I right? What should I do to store them?
To store WiFi credentials and IP settings, you would use SPIFFS. I suggest you also to store SSID and pass in a file despite WiFi core stores it.
Here is basic file operation on SPIFFS to store some data on it :
#include "FS.h"
SPIFFS.begin();
File configFile = SPIFFS.open("config.txt", "w+");
if (configFile)
{
configFile.println(IP);
configFile.println(WiFi.SSID());
// and so on ..
}
configFile.close();
Please consider the file r/w operation options declared in the SPIFFS doc.
Here is also a good config file example with JSON.

how to send push notification to arduino

I would like to know if there is anything like GCM for arduino.
I'm working on a project where I need to send push notifications to the arduino connected with WiFi shield. Arduino then will do some action based on the notification it gets or sync up with the server data.
Any advice on how I need to proceed would be helpful.
Thank you.
Yes you can send push notification to Arduino. There are multiple ways from where you can send push notification to Arduino.
I am using push notification from Parse.com.To send push notification from Parse just register with Parse.com and create account there.
Once you create account,you can send push notification from Parse dashboard.
To receive notification on Arduino you need to write sketch on Arduino, following is sketch for help.
Include Parse library to make sure following code will work.
/***************************************************************************************************************************
setup function
****************************************************************************************************************************/
void setup() {
Bridge.begin();
Serial.begin(9600);
while (!Serial);
Parse.begin("***E0uUjQkMa7nj5D5BALvzegzfyVNSG22BD2FJ", "umPSsggp5JgMFmSHfloewW5oixlM5ibt9LBS***");
// In this example, we associate this device with a pre-generated installation
Parse.getInstallationId();
Parse.startPushService();
}//setup function block
void loop() {
if (Parse.pushAvailable()) {
Serial.println("Start push");
ParsePush push = Parse.nextPush();
// Print whole JSON body
String message = push.getJSONBody();
// Serial.print("New push message size: ");
// Serial.println(message.length());
// Serial.print("New push message content: ");
Serial.println(message);
// Do something with the push
// IMPORTANT, close your push message
push.close();
checkStatus(message);
Serial.println("End Push");
}//if push notification block
} //loop
You can read documentation here https://parse.com/docs/arduino/guide#push-notifications.
I hope this will help.
You would have to run an HTTP server on the wifi shield which accepts requests, then send such a request from your outside system (command line on your desktop, IFTTT recipe, etc.)
I'm assuming this is one of the older 8bit Arduinos (since you mention the wifishield rather than something like the Yun with a Linux partition), so you will need to write a http server sketch on the Arduino.
Here is tutorial for a simple HTTP server with the wifi shield from arduino.cc
https://www.arduino.cc/en/Tutorial/WiFiWebServer

Resources