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
Related
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().
We're a bunch of noobs trying to develop an ESP32 program that could be updated OTA with ESPHttpUpdate. The code we're using is the basic:
if((WiFi.status() == WL_CONNECTED)) {
t_httpUpdate_return ret = ESPhttpUpdate.update("https://url.to/my.bin");
switch(ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
In theory, it works. The program connects to WiFi, connects to the web server, downloads the new bin and flashes it successfully... Only in a loop. Once it downloads the new firmware, it flashes it, reboots the device, and starts all over again. I guess it's a simple question of getting it to recognize if the bin on the server is newer than the one on the device - but I can't seem to figure out how to do that :) Any pointers would be appreciated - Believe me I've googled a lot :)
Thank you in advance,
Seb
What you could do in it's firmware, is that it points to the next ''update'' link.
That file is only on the server when there's an update. When you update the firmware, just change the link to point to my2.bin etc.
So each firmware points to the next update (if available).
Example:
Current Firmware: my.bin
Points to: https://url.to/my1.bin
Current Firmware: my1.bin
Points to: https://url.to/my2.bin
etc
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#define APSSID "<YOUR-SSID>"
#define APPSK "<YOUR-PASSWD>"
ESP8266WiFiMulti WiFiMulti;
int FIRMWARE_VERSION = 1; // don't forget to Change this !!!. And save the .BIN to FirmwareV1.bin, FirmwareV2.bin etc...
void checkUpdate(int firmToCheck)
{
WiFiClient client;
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); // Optional
ESPhttpUpdate.rebootOnUpdate(false); // Manual reboot after Update
String urlUPD = "http://192.168.1.100:8080/FirmwareV" + String(firmToCheck + 1) + ".bin";
t_httpUpdate_return ret = ESPhttpUpdate.update(client, urlUPD);
if (ret == HTTP_UPDATE_OK){
Serial.println("HTTP_UPDATE_OK. Reboot");
delay(1000); // Wait a second and restart
ESP.restart();
}
}
void setup(){
Serial.begin(115200);
Serial.println();
// Classic wifi connexion here. More or less complicated
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(APSSID, APPSK);
delay(5000);
Serial.print("Firmware version : ");
Serial.println(FIRMWARE_VERSION);
checkUpdate(FIRMWARE_VERSION);
// if not Server found, or not file found etc... move next
}
void loop() {
// normal stuff here !!!
//of course any time, we can do something like this : if (blablala) checkUpdate(FIRMWARE_VERSION);
}
# miken32
Sorry.
This is the KISS version of "Updating a ESP8266 or ESP32 from a Web Server" and solves the problem of devices that constantly reboot after an update: the version number indicated in the code is the same as the file on the Server (The Version number is concatenated with the file name) and the code searches at each reboot the file of the "next version". If present, firmware update and reboot. If it is absent, the code continues normally.
Ex: for "int FIRMWARE_VERSION = 1;" you must place on the server a file which will be called for example "Update_my_ESPV2.bin", concatenation of "Update_my_ESPV" + the next FIRMWARE_VERSION + ".bin"
In this new file "Update_my_ESPV2.bin", int FIRMWARE_VERSION is set to 2. At the next reboot, this firmware will search the file "Update_my_ESPV3.bin" on the Server... etc etc....
I use this process without any trouble with a Standalone Http Server (Rebex Tiny WebServer).
http://192.168.1.100:8080/ is the IP and Port of the Server. (For example). It can be anywhere. Local or remote network
On the web I've seen dozens of answers about updating via HTTP, all more complicated than that. Some solutions used a database.
This solution is much simpler , and much shorter code. Of couse, we can do better from a security point of view, but this is suitable for most uses.
Sorry for my English. I'm French ;)
ZJP
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.
I am trying to establish a connection from Amazon Web Service IoT (Internet of Things) to my Arduino Yun. I followed a tutorial about sending and receiving messages using "BasicPubSub" Arduino example, but it would say:
Failed to Connect!
-13
And this means "CONNECT_CREDENTIAL_NOT_FOUND". I have the correct credentials and information in the header file and also have the correct files, which are: xxx-certificate.pem.crt, xxx-private.pem.key, and root_certificate.pem in a directory, "/root/certs/", when I SSH into the Arduino Yun. The following is my header file:
#ifndef config_usr_h
#define config_usr_h
// Copy and paste your configuration into this file
//===============================================================
#define AWS_IOT_MQTT_HOST "xxx.iot.us-east-1.amazonaws.com" // endpoint
#define AWS_IOT_MQTT_PORT 8883
#define AWS_IOT_CLIENT_ID "client_id" // client ID
#define AWS_IOT_MY_THING_NAME "thing_name" // thing name
#define AWS_IOT_ROOT_CA_FILENAME "root_certificate.pem" // root-CA filename
#define AWS_IOT_CERTIFICATE_FILENAME "xxx-certificate.pem.crt" // your certificate filename
#define AWS_IOT_PRIVATE_KEY_FILENAME "xxx-private.pem.key" // private key
//===============================================================
// SDK config, DO NOT modify it
#define AWS_IOT_PATH_PREFIX "../certs/"
#define AWS_IOT_ROOT_CA_PATH AWS_IOT_PATH_PREFIX AWS_IOT_ROOT_CA_FILENAME // use this in config call
#define AWS_IOT_CERTIFICATE_PATH AWS_IOT_PATH_PREFIX AWS_IOT_CERTIFICATE_FILENAME // use this in config call
#define AWS_IOT_PRIVATE_KEY_PATH AWS_IOT_PATH_PREFIX AWS_IOT_PRIVATE_KEY_FILENAME // use this in config call
#endif
So my problem is that the Arduino Yun is not recognizing the files in the directory "/root/certs/" where the credentials are to connect to AWS IoT. Please help me on this issue. Thank you in advance.
For anybody else that hits this, I just encountered this when running a previously working bit of code based on the ThingSample. With the newest api (2.1.0), from what I can work out it seems that the place to put the certs folder has changed. The folder should be in /root/AWS-IoT-Python-Runtime/runtime/certs. The setup script did NOT put the certs in that folder unfortunately.
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