arduino,使用 SD 配置文件中的数据设置以太网和网络
arduino, setup ethernet & network using data from SD config file
我尝试在我的草图中添加一种动态方式来从配置文件 (config.txt) 设置以太网信息 (mac、ip、网关、子网)。所以 运行 一个网络服务器并从 sd 卡提供 htm 文件,用户可以进入设置页面,用这些信息填写表格,当发布时,网络服务器解析 http 表格并保存(更新)config.txt文件。在该系统重新启动后,为了使用新设置开始(通过读取 config.txt 文件)
除了通过读取 config.txt 文件获取参数外,我已经成功创建了所有部分(sd、以太网、网络服务器、网络客户端、从发布的表单数据创建配置文件)。
我可以逐行读取配置,我可以将行拆分为参数和值,现在我需要用读取的数据填充一些字节变量。我可以(经过一个月的 google 搜索)将 IP(十进制值)读取到字节数组。我堆栈读取 MAC ADDRESS 十六进制到字节数组。配置文件包含:
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;
我用来阅读的代码是:
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)
如有任何帮助,我将不胜感激。请不要简单地使用 Serial.print() 来回答。我发现了数百条建议,但 none 可以正常读取所有参数(十进制、十六进制、字符串)。经过一个月的努力和搜索,我想知道为什么社区中不存在如此必要和有用的示例,完全可用!!
此致
好的,这里有一套完整的例程来做你想做的事 - 我认为你误解了 char 数组与单个 char[0] 的概念。这些例程已记录在案并且不言自明。我建议不要用 ; 结束行但是使用 '\n' 在您的示例中仍然存在(您也看不到新行终止符)要获得 mac 地址,我需要三行:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
第一行比较前 4 个字符,如果为 0(表示匹配)
第二行将lineBuffer中的第五个到最后一个字符复制到目标数组中,实际上可以用作函数的参数。
文件结构应该没有;正如您必须解析的那样;和\n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
要将 char 数组转换为 int,我们使用 atoi(),将单个 char[0] 转换为单个数字,我们使用 int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'[=12=]'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'[=12=]'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'[=12=]'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'[=12=]'}; // 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] != '[=12=]') { /* 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
}
我把例程分成小的独立函数,所以它很容易适应不同的用途。很抱歉没有深入研究您的代码,因为它很难理解并且不清楚您想要做什么。
作为额外的好处,我们不使用字符串 class。这些字符串倾向于碎片化堆 - 导致 resets/crashes 而全局字符数组被编译为闪存并且不显示此行为。
我尝试在我的草图中添加一种动态方式来从配置文件 (config.txt) 设置以太网信息 (mac、ip、网关、子网)。所以 运行 一个网络服务器并从 sd 卡提供 htm 文件,用户可以进入设置页面,用这些信息填写表格,当发布时,网络服务器解析 http 表格并保存(更新)config.txt文件。在该系统重新启动后,为了使用新设置开始(通过读取 config.txt 文件)
除了通过读取 config.txt 文件获取参数外,我已经成功创建了所有部分(sd、以太网、网络服务器、网络客户端、从发布的表单数据创建配置文件)。
我可以逐行读取配置,我可以将行拆分为参数和值,现在我需要用读取的数据填充一些字节变量。我可以(经过一个月的 google 搜索)将 IP(十进制值)读取到字节数组。我堆栈读取 MAC ADDRESS 十六进制到字节数组。配置文件包含:
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;
我用来阅读的代码是:
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)
如有任何帮助,我将不胜感激。请不要简单地使用 Serial.print() 来回答。我发现了数百条建议,但 none 可以正常读取所有参数(十进制、十六进制、字符串)。经过一个月的努力和搜索,我想知道为什么社区中不存在如此必要和有用的示例,完全可用!!
此致
好的,这里有一套完整的例程来做你想做的事 - 我认为你误解了 char 数组与单个 char[0] 的概念。这些例程已记录在案并且不言自明。我建议不要用 ; 结束行但是使用 '\n' 在您的示例中仍然存在(您也看不到新行终止符)要获得 mac 地址,我需要三行:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
第一行比较前 4 个字符,如果为 0(表示匹配)
第二行将lineBuffer中的第五个到最后一个字符复制到目标数组中,实际上可以用作函数的参数。
文件结构应该没有;正如您必须解析的那样;和\n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
要将 char 数组转换为 int,我们使用 atoi(),将单个 char[0] 转换为单个数字,我们使用 int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'[=12=]'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'[=12=]'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'[=12=]'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'[=12=]'}; // 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] != '[=12=]') { /* 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
}
我把例程分成小的独立函数,所以它很容易适应不同的用途。很抱歉没有深入研究您的代码,因为它很难理解并且不清楚您想要做什么。
作为额外的好处,我们不使用字符串 class。这些字符串倾向于碎片化堆 - 导致 resets/crashes 而全局字符数组被编译为闪存并且不显示此行为。