There is no documentation in the help file as to the purpose of the Comma7IO class, just that it extends the CommaIO class.
What is the difference?
To support read and write of different
formats of external files, MorphX
features a range of different Io
classes; CommaIo for comma separated
files, Comma7Io for comma separated 7
bit files, BinaryIo for binary files
and AsciiIo for plain text files.
From this link: RE: [Axapta-Knowledge-Village] Somthing cool - IO
Run this job
static void TestComma7Io(Args _args)
{
str testString = 'ABCDEFG~ÀÁÂÃÄÅÆÇÈÉÊË~HIJKLMNOP';
str filename = #"C:\TMP\test1.txt";
str mode = 'W';
Io io;
container con;
FileIoPermission perm;
;
perm = new FileIoPermission(filename, mode);
if (!perm)
return;
perm.assert();
// BP deviation documented.
io = new Comma7Io(filename, mode);
if (io)
io.write(testString);
CodeAccessPermission::revertAssert();
}
and check the content of the file: "ABCDEFG~\300\301\302\303\304\305\306\307\310\311\312\313~HIJKLMNOP". As you see, 8-bit characters have been replaced with their octal codes.
If you replace io = new Comma7Io(filename, mode); with io = new CommaIo(filename, mode); the original string will be written to the file: "ABCDEFG~ÀÁÂÃÄÅÆÇÈÉÊË~HIJKLMNOP".
Related
Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)
I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.
I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:
mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;
and the code that I use to read is:
byte myMAC[6];
byte myIP[4];
File fset;
fset = SD.open("config.txt");
if (fset){
char ff[40];
while (fset.available()>1){
bool eol=false;
for (int i=0; !eol;i++){
ff[i]=fset.read();
if (ff[i]=='\n'){
eol=true;
}
}
String par="";
bool DONE=false;
for (int i=0; !DONE;i++){
par+=ff[i];
if (ff[i]== '='){DONE=true;}
}
String pval="";
DONE=false;
//------------------------
if (par=="ip=" ){
int x=0;
while(!DONE){
for(int i=3;i<=i+21;i++){
if(ff[i]=='.'){
myIP[x]=pval.toInt();
x++;
i++;
pval="";
}
else if(ff[i]==';' || i>20){
myIP[x]=pval.toInt();
DONE=true;
break;
}
pval+=ff[i];
}
}
}
} //while (fset.available()>1)
} //if (fset)
I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!
Best regards
Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
line one compares the first 4 characters and if it is 0 (meaning its a fit)
line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
The file structure should be with no ; as you would have to parse ; and \n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'\0'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'\0'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'\0'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'\0'}; // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
int postport=0;
// .... you can easyly implement for all other data you want to store/retrieve
// Counts the lines of a file
uint16_t countLines() {
uint16_t currentLineCount = 0;
File cfgFile = SD.open(configurationFilePath, "r");
if (!cfgFile) {
Serial.println(F("Config file open failed on read"));
} else {
while (cfgFile.available()) {
/** Lets read line by line from the file */
if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
}
cfgFile.close();
}
return currentLineCount;
}
//Load the config file from SD/SPIFFS/LittleFS
bool loadConfigFile() {
uint16_t lineCounter = countLines();
if (lineCounter <= 0) {
Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
return false;
}
else {
File cfgFile = SD.open(configurationFilePath, "r");
while (cfgFile.available()) {
strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str())); // normaly you use new line, we copy one line at a time
// Serial.println(cfgLine); /** Printing for debuging purpose */
while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
loadSingleCfgLine();
}
}
cfgFile.close();
Serial.println(F("[Success] Loaded config !"));
return true;
}
}
//Load the data of a single line into a char array
void loadSingleCfgLine() {
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
if (strncmp(cfgLine, "ip=", 3) == 0) {
strcpy (ipAddr, cfgLine + 3);
}
if (strncmp(cfgLine, "postport=", 9) == 0) {
strcpy (numBuffer, cfgLine + 9);
postport = atoi(numBuffer); // One extra step to convert to int
}
// ... easy to implement for all other data
}
I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.
I'm making a data logging system, just for fun and learning the language. If I want to save a variable ".txt" file with an INT it works fine. Now I bought an RTC and I want the name to be as following: "Data_'DATE&TIME'.txt".
As you can see in the code below, I made a function newFileName what should be doing this, but my output also below is nothing like it. And on the SD card nothing even saves.
The things in comments are things I tried.
int CURRENT_FILE = 1;
String dataString = "";
String currentFileName = "";
String currentTimeStamp = "";
void setDatumTijd(){
t = rtc.getTime();
currentTimeStamp = rtc.getDateStr();
currentTimeStamp += "--";
currentTimeStamp += rtc.getTimeStr();
currentTimeStamp.replace(':', '.');
delay(50);
}
void makeNewFile(String currentTimeStamp){
char fileName[50];
char timeStamp[20];
// sprintf(timeStamp, currentTimeStamp.c_str());
sprintf(fileName, "Data_%d.txt", currentTimeStamp.c_str());
//currentFileName = fileName;
//currentFileName.toCharArray(fileName,50);
//currentTimeStamp += ".txt";
//currentTimeStamp.toCharArray(fileName, (currentTimeStamp.length()+1));
Serial.println(fileName);
currentFileName = fileName;
File dataFile = SD.open(fileName, FILE_WRITE);
saveHeader(currentFileName, currentTimeStamp);
dataFile.close();
}
enter image description here
The library that comes with Arduino doesn't support long filenames, it only supports "8 bytes for filename"."3 bytes for file type", and in your case, it is clearly exceeding that limit.
This all is due to the reason that most of the Arduino boards has lesser RAM.
But if you still want to go ahead, you can use the following library.
https://github.com/greiman/SdFat
This library supports Long File names and if I remember correctly, the Arduino Standard Library is also the wrapper of the library created by the same author.
https://forum.arduino.cc/index.php?topic=58549.msg421288#msg421288
Note: Long File Names consumes much RAM.
Background
I've been struggling with decrypting an apparently well-formed cipher text for about a day. Assume we've got the following hex-encoded cipher text which contains exactly 160 characters thereby having 80 bytes.
QString c = "1BFAC407AF0D440A2D6176C0B5D125AA96088490299AC18C74623C0EF1BB1372E554FC4150A8066220E943697BE2491D8AE13AA036B298425AC510A8A917D59EBB69708B9040AB3A84C63043EAD4AB07";
QString k = CryptoUtils::hexEncode("abc");
QString p = CryptoUtils::decrypt(c, k);
qDebug() << p;
Provided we're using AES 256, AFAIK, the key must be of length 32 bytes and cipher text of a length of multiple of 16 bytes, which all these consditions are met regarding my snippet code.
Please note that I'm using SHA256 feeding with a pass phrase to generate a 32 bytes key. So, this ensures that all keys are of length 32 bytes.
Full source codes of those function can be found on my repo on GitHub (at branch Part1).
My Question
When I want to run this code, my app crashes. Here's the exception:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
The program has unexpectedly finished.
I searched around about this problem and figured out it could be because of the trailing \0 once you encrypted the plain text. However, I couldn't just solve the problem. Please help me out, it's just driving me crazy.
Full source codes of those function can be found on my repo on GitHub
I'd make these changes at minimum:
QString CryptoUtils::encrypt(QString text, QString keyhex)
{
...
// name the variable, kill the memory leak
SHA256 sha256;
StringSource ss1(decodedKey, size, true, new HashFilter(sha256, new ArraySink(key, AES::MAX_KEYLENGTH)));
...
// name the variable
StringSource ss2(plain, true, new StreamTransformationFilter(Encryptor, new HexEncoder(new StringSink(encrypted))));
// verify embedded NULLs don't affect results
QString qs = QString::fromStdString(encrypted);
assert(qs.length() == encrypted.length());
}
And:
QString CryptoUtils::decrypt(QString text, QString keyhex)
{
// bad karma here...
string encrypted = text.toStdString();
assert(encrypted.length() == text.length());
...
// name the variable, kill the memory leak
SHA256 sha256;
StringSource ss1(decodedKey, size, true, new HashFilter(sha256, new ArraySink(key, AES::MAX_KEYLENGTH)));
...
// name the variable,
StringSource ss2(encrypted, true, new HexDecoder(new StreamTransformationFilter(Decryptor, new StringSink(plain))));
// verify embedded NULLs don't affect results
QString qs = QString::fromStdString(plain);
assert(qs.length() == plain.length());
}
The hexEncode function seems to misbehave:
QString CryptoUtils::hexEncode(QString text)
{
byte *bytearray = (byte *) text.toLatin1().data();
int length = text.toLatin1().length();
return hexEncode(bytearray, length);
}
Should be replaced with:
QString CryptoUtils::hexEncode(QString text)
{
byte *bytearray = (byte *) text.toStdString().data();
int length = text.length();
return hexEncode(bytearray, length);
}
I have the below code to read a light sensor, convert to lux, concat with "lux." and send it to my SmartThings cloud.
Ultimately I want a value sent to SmartThings formatted like lux.110
void checkLux() {
float logLux = analogRead(lightPIN) * logRange / rawRange;
int luxValue = pow(10, logLux);
String statusUpdate = "lux." + luxValue;
Serial.println(statusUpdate);
smartthing.send(statusUpdate);
delay(1000);
}
This above code spits out some weird combination of characters to the serial monitor and doesnt print lux. or the luxvalue.
If I add this line String luxString = "lux."; and modify the line below, it all works great. Any thoughts on why I need to declare this string separately. According to the documentation either should work fine.
Also if there are any suggestions on variable savings within this block of code. I am not that great at it yet.
As Arduino just uses C++ most C++ functions will also work so dont just limit yourself to Arduino's reference pages.
Apparently the String constructor doesn't support numbers, you must convert them using the String() function first as seen here.
Alternatively I think you can append a string like this:
String statusUpdate = "lux.";
statusUpdate += luxValue;
as seen here,
which is the same as using String's concat function.
statusUpdate.concat(luxValue);
I have:
Arduino MEGA 2560;
Ethernet+SD shield http://www.amazon.com/dp/B0022TWQ22/?tag=stackoverfl08-20 ;
SD card 2GB FAT.
SD contains 400 files with names 00000000; 0000001; 0000002; ... 00000098; 0000099; 0000100; ... 00000398; 00000399.
I need to construct String var which will contain all the Filenames separated by ";" like this:
sdata = "0000001;0000002;0000003 ... 00000398;00000399;";
Code:
#include <SdFat.h>
#include <SPI.h>
const uint16_t chipSelect = SS;
char cnamefile[9];
String sdata="";
SdFat sd;
SdFile file;
void setup() {
Serial.begin(9600);
Serial.println("hi");
sdata="";
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
Serial.println("List files");
while (file.openNext(sd.vwd(), O_READ)) {
file.getFilename(cnamefile);
file.close();
sdata = sdata + String(cnamefile) + ";";
}
Serial.print(sdata);
}
void loop() {
}
Listening to the COM port i see:
hi
List files
00000005;00000006;00000007;00000008;00000009;00000010;00000011;00000012;00000013;00000014;00000015;00000016;00000017;00000018;00000019;00000020;00000021;00000022;00000023;00000024;00000025;00000026;00000027;00000028;00000029;00000030;00000031;00000032;00000033;00000034;00000035;00000036;00000037;00000038;00000039;00000040;00000041;00000042;00000043;00000044;00000045;00000046;00000047;00000048;00000049;00000050;00000051;00000052;00000053;00000054;00000055;00000056;00000057;00000058;00000059;00000060;00000061;00000062;00000063;00000064;00000065;00000066;00000067;00000068;00000069;00000070;00000071;00000072;00000073;00000074;00000075;00000076;00000077;00000078;
How to fix this problem and put all filenames in one variable?
Information for: 400 names and 400 ";" its 3600 bytes. When i try to read any file and put all its contents (more than 3600 bytes) in "String sdata" it works normally. Problem only with listing.
Please help me in sorting out this issue.
This seems about the correct place that your program will fail. This innocent line is your problem:
sdata = sdata + String(cnamefile) + ";";
String concatentation like this will use 2X the memory of the sdata for a short moment. This is the how you should view the sequence of operations in that one line
// compiler has done this for you:
String temp1234 = sdata + String();
// note that at this moment, memory usage is now 2x sdata
String temp1235 = temp1234 + ";";
// now you can have 3x the memory used
// assignment to variable
sdata = temp1235;
// now can delete temporary variable
// compiler will do this
//temp1234.delete()
//temp1235.delete()
You are trying to create strings up to 3k bytes but have only 8k total RAM, so will not be able to do the above.
This demonstrates a couple of points about Strings. Your concatenation above on one line, is not necessarily better than this two line form:
sdata = sdata + String(cnamefile);
sdata = sdata + ";";
In this second form, you are ensured there will only be one temporary variable for the intermediate result.
This leads to the next hint. You should be thinking how am I going to escape the temporary variable. That is why we have += operator. Your best chance is to concatenate like this:
sdata += String(cnamefile);
sdata += ";";
If the += operator is available on the String class, the compiler will use this. That operator may be able to use a more memory efficient way of concatenation. For example, if the String was preallocated with some extra memory, then it could just place the new characters into the existing buffer.
In general this is a great learning method about strings in constrained memory spaces, because you must understand some compiler internals and operator details that are often ignored in large CPU environments.
Given the sizes you are suggesting, you will probably only be able to fit in RAM if you change to an approach of pre-constructing a String buffer and filling it with the file names. In other words: don't use String on a microcontroller.