I'm trying to store some hex color palettes in the ESP8266's SPIFFS but when I try to retrieve them I get some weird chars. The code and the log output are below:
Code (ino file):
#include "FS.h" // https://github.com/esp8266/Arduino/
#include <JsonParser.h> // https://github.com/henrikekblad/ArduinoJson
#include <JsonGenerator.h> // https://github.com/henrikekblad/ArduinoJson
using namespace ArduinoJson;
void setup(void) {
Serial.begin(9600);
SPIFFS.format();
SPIFFS.begin();
writeTxtFile("/palettes/warm.json", "[\"ffffff\",\"ed1414\",\"f6f336\",\"ff7e15\"]");
writeTxtFile("/palettes/pastel.json", "[\"ffffff\",\"ff7b7b\",\"8fff70\",\"7878ff\"]");
readPalettes();
}
void loop(void) {
}
bool readPalettes(void) {
bool succeeded = true;
Dir dir = SPIFFS.openDir("/palettes");
while (dir.next()) {
File palette = dir.openFile("r");
if (!readFile(palette, loadPaletteJson)) succeeded = false;
palette.close();
yield();
}
return succeeded;
}
bool loadPaletteJson(char* json, String fileName) {
Serial.print(fileName);Serial.print(F(": "));Serial.println(json);
Parser::JsonParser<5> parser; // one palette is 1+4*1=5 tokens
Parser::JsonArray p = parser.parseArray(json);
if (p.success()) {
//Store the palettes in a vector using a specific structure
return true;
} else {
Serial.println(F("JSON parsing failed"));
}
return false;
}
bool readFile(File file, std::function<bool (char* json, String fileName)> callback) {
if (file) {
String fileName = file.name();
size_t fileSize = file.size();
Serial.print(F("Reading "));Serial.print(fileSize);Serial.print(F(" bytes from "));Serial.println(fileName);
if (fileSize <= 1024) {
char buf[fileSize];
file.readBytes(buf, fileSize);
return callback(buf, getBaseName(fileName));
} else {
Serial.println(F("file size is too large"));
}
} else {
Serial.println(F("file open failed"));
}
return false;
}
String getBaseName(String fileName) {
return fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('.'));
}
bool writeTxtFile(String path, String content) {
File file = SPIFFS.open(path, "w");
if (file) {
Serial.print(F("Writing content: "));Serial.print(content);Serial.print(F(" to: "));Serial.println(path);
file.print(content);
file.close();
return true;
} else {
Serial.print(F("file open failed: "));Serial.println(path);
}
return false;
}
Arduino's IDE console output:
Writing content: ["ffffff","ed1414","f6f336","ff7e15"] to: /palettes/warm.json
Writing content: ["ffffff","ff7b7b","8fff70","7878ff"] to: /palettes/pastel.json
Reading 37 bytes from /palettes/warm.json
warm: ["ffffff","ed1414","f6f336","ff7e15"]⸮?8⸮?tu #
JSON parsing failed
Reading 37 bytes from /palettes/pastel.json
pastel: ["ffffff","ff7b7b","8fff70","7878ff"]⸮?8⸮?tu #
JSON parsing failed
I don't understand why those chars (⸮?8⸮?tu #) are added to a limited sized buffer. I tried to replace my buffer (char buf[fileSize];) with a unique pointer like this: std::unique_ptr<char[]> buf(new char[fileSize]); (in the function readFile) using buf.get() in the next 2 statements but with the exact same result.
Am I missing something?
EDIT:
As #leetibbett mentioned below, I'm writing a String while trying to read bytes. here is the correction for readFile function:
bool readFile(File file, std::function<bool (char* json, String fileName)> callback) {
if (file) {
String fileName = file.name();
size_t fileSize = file.size();
Serial.print(F("Reading "));Serial.print(fileSize);Serial.print(F(" bytes from "));Serial.println(fileName);
if (fileSize <= 1024) {
String content = file.readStringUntil('\n');
return callback((char*)content.c_str(), getBaseName(fileName));
} else {
Serial.println(F("file size is too large"));
}
} else {
Serial.println(F("file open failed"));
}
return false;
}
Related
When trying to unzip an image file using libzip, I have run across the issue where in the image data, I hit a null byte and libzip zip_fread sees this as EOF and stops reading the file, resulting in a corrupted image. What is the best way to get handle the null byte when reading an image and extract the full image?
To clarify, text only files extract perfectly fine.
Below is the code used:
int FileHandler::ExtractFiles(std::string& path, std::string& file, bool is_test)
{
int err = 0;
std::string fullPath = path + "\\" + file;
zip* za = zip_open(fullPath.c_str(), 0, &err);
struct zip_stat st;
zip_stat_init(&st);
int number_of_entries = zip_get_num_entries(za, NULL);
for (zip_uint64_t i = 0; i < number_of_entries; ++i)
{
const char* name = zip_get_name(za, i, NULL);
std::string s_name = name;
size_t pos;
std::string backsl = "\\";
while ((pos = s_name.find(47)) != std::string::npos)
{
s_name.replace(pos, 1, backsl);
}
std::string fullFilePath = path + "\\" + s_name;
if(!is_test)
printf("Extracting: %s...\n", s_name.c_str());
std::string fullDir;
size_t found;
found = fullFilePath.find_last_of("\\");
if (found != std::string::npos)
{
fullDir = fullFilePath.substr(0, found);
}
struct zip_stat ist;
zip_stat_init(&ist);
zip_stat(za, name, 0, &ist);
char* contents = new char[ist.size];
zip_file* f = zip_fopen(za, name, 0);
// zip_fread to contents buffer
zip_fread(f, contents, ist.size);
if (CreateDirectory(fullDir.c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError())
{
// writing buffer to file
if (!std::ofstream(fullFilePath).write(contents, ist.size))
{
return EXIT_FAILURE;
}
}
zip_fclose(f);
}
zip_close(za);
return EXIT_SUCCESS;
}
gerum was able to point me in the right direction. For anyone that is wondering or has the same issue, I had to open the ofstream in binary mode and that resolved the issue.
Original code:
// writing buffer to file
if (!std::ofstream(fullFilePath).write(contents, ist.size))
{
return EXIT_FAILURE;
}
Solution:
// writing buffer to file
if (!std::ofstream(fullFilePath, std::ios::binary).write(contents, ist.size))
{
return EXIT_FAILURE;
}
I'm trying to read a .wav file that I have from my .cpp file that I have in my iOS Xcode project. I have searched and I can't find the answer to why it is not reading it well. I putted in all the possible directories and still doesn't work. This is the code that is calling the .wav file:
#define EXPORT __attribute__((visibility("default")))
EXPORT
string getAudioWmark()
{
std::string result;
result = get_watermark("piano2.wav", "");
return (result.c_str());
}
get_watermark method (from wget.cc):
std::string get_watermark (const string& infile, const string& orig_pattern)
{
std::string patterns;
WavData wav_data;
Error err = wav_data.load (infile);
if (err)
{
error ("audiowmark: error loading %s: %s\n", infile.c_str(), err.message());
return "1";
}
if (Params::test_truncate)
{
const size_t want_n_samples = wav_data.sample_rate() * wav_data.n_channels() * Params::test_truncate;
vector<float> short_samples = wav_data.samples();
if (want_n_samples < short_samples.size())
{
short_samples.resize (want_n_samples);
wav_data.set_samples (short_samples);
}
}
int result;
if (wav_data.sample_rate() == Params::mark_sample_rate)
{
result = decode_and_report (wav_data, orig_pattern);
if (PatternStorage::getInstance().getPatterns() != "") {
patterns = PatternStorage::getInstance().getPatterns();
}
return patterns;
}
else
{
result = decode_and_report (resample (wav_data, Params::mark_sample_rate), orig_pattern);
if (PatternStorage::getInstance().getPatterns() != "") {
patterns = PatternStorage::getInstance().getPatterns();
}
return patterns;
}
}
Here is the directories from my Xcode project (the code above is from native-lib.cpp:
I'm working on a project with Flash data saving. I'm using SPIFFS library for ESP32, I'm currently attempting to store the data from each line into a String. Since I have control of how many content can go into the file, it won't need more than 3 Strings to store the data. I could easily manage to store the first line content using readStringUntil. But I can't manage to get the content from 2 and 3 line.
For the first line I'm using this code:
//Pegar a primeira linha do arquivo, onde será armazenado o nome do WIFI (ssid)
void first_line (){
file = SPIFFS.open("/wifi.txt", "r");
while (file.available()) {
String first_line = file.readStringUntil('\n');
Serial.print(first_line);
break;
}
file.close();
}
I'm writing the code into the File with this function:
// Escrever mensagem dentro do arquivo
void write_file_info(String message) {
file = SPIFFS.open("/wifi.txt", FILE_WRITE);
if (!file){
Serial.println("Error opening file");
return;
}else{
Serial.println("Success opening file");
}
if (file.println(message)){
Serial.println("File was written");
}else{
Serial.println("File was not written");
}
file.close();
}
And I'm using Append to add the second and third line:
void append_file_info (String message){
file = SPIFFS.open("/wifi.txt", FILE_APPEND);
if (!file){
Serial.println("Erro ao realizar APPEND ao arquivo");
}
if (file.println(message)){
Serial.println("File was added");
}else{
Serial.println("File was not added");
}
file.close();
}
This is the current output, file size is just for manage and "content inside file" is just for reference:
File size: 37
Content inside file:
first line
second line
thrid line
This is how I'm reading the file:
void read_file_info() {
file = SPIFFS.open("/wifi.txt");
Serial.print("\nFile size: ");
Serial.println(file.size());
Serial.print("Content inside file: \n");
while (file.available()){
Serial.write(file.read());
}
Serial.println("\n");
file.close();
delay(3000);
}
I thought on trying to read the data after '\n', but couldn't find any documentation on reading after certain string.
I tried creating a buffer and splitting it later, the output from the buffer is correct but I can't split it into strings correctly:
void second_line (){
file = SPIFFS.open("/wifi.txt", "r");
char buffer[64];
while (file.available()) {
int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
buffer[l] = 0;
Serial.println(buffer);
}
file.close();
}
It would be simpler using vector:
#include <SPIFFS.h>
using namespace std;
void setup() {
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
File file = SPIFFS.open("/wifi.txt");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
vector<String> v;
while (file.available()) {
v.push_back(file.readStringUntil('\n'));
}
file.close();
for (String s : v) {
Serial.println(s);
}
}
void loop() {}
Use v[0] to get first line, v[1] for second line, v[2] for third line and so on.
I could manage to get it working like this:
void all_lines (){
file = SPIFFS.open("/wifi.txt", "r");
int i = 0;
char buffer[64];
String line_one, line_two, line_three;
while (file.available()) {
int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
buffer[l] = 0;
if (i == 0) {
line_one = buffer;
}
if (i == 1) {
line_two = buffer;
}
if (i == 2) {
line_three = buffer;
}
i++;
if (i == 3){
break;
}
}
file.close();
}
I found this code snippet for downloading file:
wxURL url(wxT("http://www.example.com/file.txt"));
if(url.GetError()==wxURL_NOERR)
{
wxString htmldata;
wxInputStream *in = url.GetInputStream();
if(in && in->IsOk())
{
wxStringOutputStream html_stream(&htmldata);
in->Read(html_stream);
wxLogMessage(htmldata);
}
delete in;
}
But fistly it just logs content of file and only for text-files. But I need to download *.exe file to execute it later. So I need to adapt this code to work with binary data, and save this data to file on the disk. Too many Streams used here for my understanding what's going on here. Please help.
I have written below code previously...
This will work fine to download binary files in any platforms.
/** START */
// ex) ht tp://mysite.com/mypath.jpg
wxString path = wxT("/mypath.jpg");
wxString server = wxT("mysite.com");
wxHTTP http;
http.SetHeader(_T("Content-type"), contentType);
http.SetTimeout(10);
// wxString imageFilePath = wxT("/tmp/image.jpg");
wxFileOutputStream output(imageFilePath);
wxDataOutputStream store(output);
if (http.Connect(server, 80))
{
wxInputStream *stream;
stream = http.GetInputStream(path);
if (stream == NULL)
{
output.Close();
}
else
{
unsigned char buffer[1024];
int byteRead;
// receive stream
while (!stream->Eof())
{
stream->Read(buffer, sizeof(buffer));
store.Write8(buffer, sizeof(buffer));
byteRead = stream->LastRead();
if (byteRead <= 0)
{
break;
}
}
output.Close();
}
}
else
{
output.Close();
}
FILE.CPP: I am using this file and to use this I have created object "myFile"
/*
SD - a slightly more friendly wrapper for sdfatlib
This library aims to expose a subset of SD card functionality
in the form of a higher level "wrapper" object.
License: GNU General Public License V3
(Because sdfatlib is licensed with this.)
(C) Copyright 2010 SparkFun Electronics
*/
#include <SD.h>
/* for debugging file open/close leaks
uint8_t nfilecount=0;
*/
File::File(SdFile f,
const char *n) {
// oh man you are kidding me, new() doesnt exist? Ok we do it by hand!
_file=(SdFile *)malloc(sizeof(SdFile));
if (_file) {
memcpy(_file, &f, sizeof(SdFile));
strncpy(_name, n, 12);
_name[12]=0;
/* for debugging file open/close leaks
nfilecount++;
Serial.print("Created \"");
Serial.print(n);
Serial.print("\": ");
Serial.println(nfilecount, DEC);
*/
}
}
File::File(void) {
_file=0;
_name[0]=0;
//Serial.print("Created empty file object");
}
File::~File(void) {
// Serial.print("Deleted file object");
}
// returns a pointer to the file name
char *File::name(void) {
return _name;
}
// a directory is a special type of file
boolean File::isDirectory(void) {
return (_file && _file->isDir());
}
size_t File::write(uint8_t val) {
return write(&val, 1);
}
size_t File::write(const uint8_t *buf,
size_t size) {
size_t t;
if (!_file) {
setWriteError();
return 0;
}
_file->clearWriteError();
t=_file->write(buf,
size);
if (_file->getWriteError()) {
setWriteError();
return 0;
}
return t;
}
int File::peek() {
if (! _file) return 0;
int c=_file->read();
if (c !=-1) _file->seekCur(-1);
return c;
}
int File::read() {
if (_file) return _file->read();
return -1;
}
// buffered read for more efficient, high speed reading
int File::read(void *buf,
uint16_t nbyte) {
if (_file) return _file->read(buf, nbyte);
return 0;
}
int File::available() {
if (! _file) return 0;
uint32_t n=size() - position();
return n > 0X7FFF ? 0X7FFF: n;
}
void File::flush() {
if (_file) _file->sync();
}
boolean File::seek(uint32_t pos) {
if (! _file) return false;
return _file->seekSet(pos);
}
uint32_t File::position() {
if (! _file) return -1;
return _file->curPosition();
}
uint32_t File::size() {
if (! _file) return 0;
return _file->fileSize();
}
void File::close() {
if (_file) {
_file->close();
free(_file);
_file=0;
/* for debugging file open/close leaks
nfilecount--;
Serial.print("Deleted ");
Serial.println(nfilecount, DEC);
*/
}
}
File::operator bool() {
if (_file) return _file->isOpen();
return false;
}
I am trying to create a SD card read write package using Simulink. What happens is here is I would take SD.cpp and SD.h and other required files and create a wrapper file which consists of required operation (i.e. reading and writing to SD card) and I am getting the following errors
SDcard_wrapper.cpp: In function 'void SDcard_Update_wrapper(const boolean_T*, real_T*, const uint8_T*, int_T)':
./SDcard_wrapper.cpp:113: error: no match for 'operator=' in 'myFile = SDClass::open(const char*, uint8_t)(((const char*)"test.txt"), 19u)'
C:\Users\Samanth\Documents\MATLAB\newSD-trail/utility/SdFat.h:135: note: candidates are: SdFile& SdFile::operator=(const SdFile&)
./SDcard_wrapper.cpp:123: error: could not convert 'myFile' to 'bool'
Following is the SDcard_wrapper.cpp error locations:
myFile = SD.open("test.txt", FILE_WRITE); //This is the line where I am getting 1st error
if (myFile) // this is the point where I am getting the error: could not convert 'myFile' to 'bool'
{
Serial.print("Writing to test.txt...");
myFile.println("testing 1, 2, 3.");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
File SDClass::open(const char* filepath, uint8_t mode) {
/*
Open the supplied file path for reading or writing.
The file content can be accessed via the `file` property of
the `SDClass` object--this property is currently
a standard `SdFile` object from `sdfatlib`.
Defaults to read only.
If `write` is true, default action (when `append` is true) is to
append data to the end of the file.
If `append` is false then the file will be truncated first.
If the file does not exist and it is opened for writing the file
will be created.
An attempt to open a file for reading that does not exist is an
error.
*/
int pathidx;
// do the interative search
SdFile parentdir = getParentDir(filepath, &pathidx);
// no more subdirs!
filepath += pathidx;
if (!filepath[0]) {
// it was the directory itself!
return File(parentdir, "/");
}
// Open the file itself
SdFile file;
// failed to open a subdir!
if (!parentdir.isOpen())
return File();
// there is a special case for the Root directory since its a static dir
if (parentdir.isRoot()) {
if (!file.open(SD.root, filepath, mode)) {
// failed to open the file :(
return File();
}
// dont close the root!
} else {
if (!file.open(parentdir, filepath, mode)) {
return File();
}
// close the parent
parentdir.close();
}
if (mode & (O_APPEND | O_WRITE))
file.seekSet(file.fileSize());
return File(file, filepath);
}
class SdFile : public Print // This is the line pointed above in the second error just for information SdFat.h:135: note: candidates are: SdFile& SdFile::operator=(const SdFile&)
{
public:
/** Create an instance of SdFile. */
SdFile(void): type_(FAT_FILE_TYPE_CLOSED) {}
/**
* writeError is set to true if an error occurs during a write().
* Set writeError to false before calling print() and/or write() and check
* for true after calls to print() and/or write().
*/
//bool writeError;
/**
* Cancel unbuffered reads for this file.
* See setUnbufferedRead()
*/
void clearUnbufferedRead(void) {
flags_ &= ~F_FILE_UNBUFFERED_READ;
}
uint8_t close(void);
uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
uint8_t createContiguous(SdFile* dirFile, const char* fileName, uint32_t size);
/** \return The current cluster number for a file or directory. */
uint32_t curCluster(void) const {
return curCluster_;
}
/** \return The current position for a file or directory. */
uint32_t curPosition(void) const {
return curPosition_;
}
static void dateTimeCallback(void(*dateTime)(uint16_t* date, uint16_t* time)) {
dateTime_ = dateTime;
}
/**
* Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel(void) {
// use explicit zero since NULL is not defined for Sanguino
dateTime_ = 0;
}