如何完善和扩展 Arduino 有限状态机
How to Refine and Expand Arduino Finite State Machine
我正在努力在我的 Arduino 程序中建立一个状态机,我想我有一个足够简单的程序来学习有限状态机。目前我遇到的问题是每次程序更改状态时,它都会用它的状态或正在记录的数据淹没串行监视器。我创建了一些在非常有限的级别上工作的东西,但我觉得我无法扩展它,因为这段代码只有在我有 3 个状态时才有效。但是如果我想要 4 或 5 个状态呢?
我希望在程序继续 运行 时每次状态更改时在串行监视器中只提示一次。
例如,
- 如果程序空闲(状态 0),串口监视器什么也不显示。
- 如果程序只读,提示串行监视器(状态 1)一次,而不是每次执行读取时。
- 如果因为SPST开关状态改变导致程序正在读取和记录,则提示串口监视器(状态2)一次,而不是每次读取和记录时都提示。
我还想知道如何更好地解决这个问题,因为我觉得这似乎只适用于 3 态示例。如果我添加更多开关或更多状态会怎样?
快捷编程功能:
- 等待 100 毫秒。
- LED1 状态高
- LED2 状态低
- 如果 SPST 开关为高电平,读取模拟输入引脚并 return 回到空闲状态。(此时会有一个显示器挂起,无论是否正在记录数据都会显示值)。
- LED1 状态低
- LED2 状态低
- 如果 SPST 开关为低电平,则将读取的数据记录到文件中。
- 如果这是第一次,请使用 unix 名称创建一个文件。
- 如果这不是第一次(文件存在),打开它并附加到它。
- LED1 状态低
- LED2 状态为高
我有这样的工作,但感觉效率低下,我是幸运的。我一直在努力执行有限状态机。
void setup() {
Serial.begin(57600); // Initiate serial
delay(100);
// Set pinmodes
// Start and adjust RTC
// Begin RTC
delay(100);
// Initialize SD Card
}
void loop() {
// Read to see if the switch has changed states
PIN_STATE = digitalRead(PIN_SWITCH);
currentMillis = millis();
// State 0 - Idle
if (currentMillis - previousMillis < interval) {
STATE = STATE; // State flag set
led_status(HIGH, LOW);
digitalWrite(PIN_READ, LED_READ);
}
// State 1 - Read Only
if ((PIN_STATE == HIGH) && (currentMillis - previousMillis >= interval)) {
if (CHANGE_STATE) {
STATE = 1; // State flag set
Serial.println("Changed state: Read");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, LOW); // Update LEDs
read(); // Read the pins
CHANGE_STATE = false;
}
//State 2 - Read and Record
else if ((PIN_STATE == LOW) && (currentMillis - previousMillis >= interval)) {
if (!CHANGE_STATE) {
Serial.println("Changed state: Read and Record");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, HIGH); // Update LEDs
read(); // Read the pins
if (STATE < 2) { // Was a file already created?
// Create new file
}
filename = filename;
open(); // Open the file
write(); // Write to the file
close(); // Close the file
STATE = 2; // State flag set
CHANGE_STATE = true;
}
}
void read() {
//Read Values
//Calculate Values
}
void open() {
// Open the file
}
void write() {
// Write to file
}
void close() {
// Close the file
}
void led_status(int led_one, int led_two) {
// Change the status of LEDS
更新,解决。
从 Arcadien 下面提供的文章中,我能够重新思考程序流程并将其重写为 switch case 状态机。
// Declare the states
enum state {
_readState,
_displayState,
_createState,
_openState,
_writeState,
_saveState,
_errorState
};
void loop() {
pinState = digitalRead(pinSwitch);
// Green LED HIGH
// Yellow LED LOW
currentMillis = millis();
if ((currentMillis - previousMillis) >= interval) {
switch (_currentState) {
case _readState:
//Serial.println("Current State: _readState");
// Green LED LOW
_currentState = _readState;
previousMillis = currentMillis;
//Read Values
case _displayState:
//Serial.println("Current State: _displayState");
_currentState = _displayState;
previousMillis = currentMillis;
if (pinState != LOW) { // If not recording, then break
filename = "";
_currentState = _readState;
break;
};
case _createState:
//Serial.println("Current State: _createState");
// Yellow LED HIGH
_currentState = _createState;
previousMillis = currentMillis;
if (filename == NULL) {
//Serial.print("Creating new file... ");
DateTime now = rtc.now();
filename = String(now.unixtime(), DEC);
filename = filename + ".txt";
//Serial.print(filename);
//Serial.println(" created!");
//Serial.print("Writing header to file... ");
//dataFile = SD.open(filename, FILE_WRITE);
dataFile = SD.open(filename, O_WRITE | O_CREAT);
dataFile.println("Time(ms), TPS, AFR");
//Serial.println("header written!");
dataFile.close();
};
case _openState:
//Serial.println("Current State: _openState");
_currentState = _openState;
dataFile = SD.open(filename, O_CREAT | O_APPEND | O_WRITE); // Open filename.txt
case _writeState:
//Serial.println("Current State: _writeState");
_currentState = _writeState;
// if the file is available, write to it:
if (dataFile) {
// Create a single line string of data
dataFile.println(_data); // Write data
}
// if the file didn't open, throw errors
else {
Serial.print("error opening ");
Serial.println(filename);
_currentState = _errorState;
}
case _saveState:
//Serial.println("Current State: _saveState");
_currentState = _saveState;
dataFile.flush();
dataFile.close();
_currentState = _readState;
break;
case _errorState:
_currentState = _errorState;
// Red LED HIGH
}
}
// Return to the begining
}
你应该看看这里的其他答案,比如 this one。粗略地说,您必须将机器及其状态分开。嵌入式代码通常需要紧凑和高效,状态机可以使用转换表来实现。
我正在努力在我的 Arduino 程序中建立一个状态机,我想我有一个足够简单的程序来学习有限状态机。目前我遇到的问题是每次程序更改状态时,它都会用它的状态或正在记录的数据淹没串行监视器。我创建了一些在非常有限的级别上工作的东西,但我觉得我无法扩展它,因为这段代码只有在我有 3 个状态时才有效。但是如果我想要 4 或 5 个状态呢?
我希望在程序继续 运行 时每次状态更改时在串行监视器中只提示一次。 例如,
- 如果程序空闲(状态 0),串口监视器什么也不显示。
- 如果程序只读,提示串行监视器(状态 1)一次,而不是每次执行读取时。
- 如果因为SPST开关状态改变导致程序正在读取和记录,则提示串口监视器(状态2)一次,而不是每次读取和记录时都提示。
我还想知道如何更好地解决这个问题,因为我觉得这似乎只适用于 3 态示例。如果我添加更多开关或更多状态会怎样?
快捷编程功能:
- 等待 100 毫秒。
- LED1 状态高
- LED2 状态低
- 如果 SPST 开关为高电平,读取模拟输入引脚并 return 回到空闲状态。(此时会有一个显示器挂起,无论是否正在记录数据都会显示值)。
- LED1 状态低
- LED2 状态低
- 如果 SPST 开关为低电平,则将读取的数据记录到文件中。
- 如果这是第一次,请使用 unix 名称创建一个文件。
- 如果这不是第一次(文件存在),打开它并附加到它。
- LED1 状态低
- LED2 状态为高
我有这样的工作,但感觉效率低下,我是幸运的。我一直在努力执行有限状态机。
void setup() {
Serial.begin(57600); // Initiate serial
delay(100);
// Set pinmodes
// Start and adjust RTC
// Begin RTC
delay(100);
// Initialize SD Card
}
void loop() {
// Read to see if the switch has changed states
PIN_STATE = digitalRead(PIN_SWITCH);
currentMillis = millis();
// State 0 - Idle
if (currentMillis - previousMillis < interval) {
STATE = STATE; // State flag set
led_status(HIGH, LOW);
digitalWrite(PIN_READ, LED_READ);
}
// State 1 - Read Only
if ((PIN_STATE == HIGH) && (currentMillis - previousMillis >= interval)) {
if (CHANGE_STATE) {
STATE = 1; // State flag set
Serial.println("Changed state: Read");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, LOW); // Update LEDs
read(); // Read the pins
CHANGE_STATE = false;
}
//State 2 - Read and Record
else if ((PIN_STATE == LOW) && (currentMillis - previousMillis >= interval)) {
if (!CHANGE_STATE) {
Serial.println("Changed state: Read and Record");
}
previousMillis = currentMillis; // Remember the time
led_status(LOW, HIGH); // Update LEDs
read(); // Read the pins
if (STATE < 2) { // Was a file already created?
// Create new file
}
filename = filename;
open(); // Open the file
write(); // Write to the file
close(); // Close the file
STATE = 2; // State flag set
CHANGE_STATE = true;
}
}
void read() {
//Read Values
//Calculate Values
}
void open() {
// Open the file
}
void write() {
// Write to file
}
void close() {
// Close the file
}
void led_status(int led_one, int led_two) {
// Change the status of LEDS
更新,解决。 从 Arcadien 下面提供的文章中,我能够重新思考程序流程并将其重写为 switch case 状态机。
// Declare the states
enum state {
_readState,
_displayState,
_createState,
_openState,
_writeState,
_saveState,
_errorState
};
void loop() {
pinState = digitalRead(pinSwitch);
// Green LED HIGH
// Yellow LED LOW
currentMillis = millis();
if ((currentMillis - previousMillis) >= interval) {
switch (_currentState) {
case _readState:
//Serial.println("Current State: _readState");
// Green LED LOW
_currentState = _readState;
previousMillis = currentMillis;
//Read Values
case _displayState:
//Serial.println("Current State: _displayState");
_currentState = _displayState;
previousMillis = currentMillis;
if (pinState != LOW) { // If not recording, then break
filename = "";
_currentState = _readState;
break;
};
case _createState:
//Serial.println("Current State: _createState");
// Yellow LED HIGH
_currentState = _createState;
previousMillis = currentMillis;
if (filename == NULL) {
//Serial.print("Creating new file... ");
DateTime now = rtc.now();
filename = String(now.unixtime(), DEC);
filename = filename + ".txt";
//Serial.print(filename);
//Serial.println(" created!");
//Serial.print("Writing header to file... ");
//dataFile = SD.open(filename, FILE_WRITE);
dataFile = SD.open(filename, O_WRITE | O_CREAT);
dataFile.println("Time(ms), TPS, AFR");
//Serial.println("header written!");
dataFile.close();
};
case _openState:
//Serial.println("Current State: _openState");
_currentState = _openState;
dataFile = SD.open(filename, O_CREAT | O_APPEND | O_WRITE); // Open filename.txt
case _writeState:
//Serial.println("Current State: _writeState");
_currentState = _writeState;
// if the file is available, write to it:
if (dataFile) {
// Create a single line string of data
dataFile.println(_data); // Write data
}
// if the file didn't open, throw errors
else {
Serial.print("error opening ");
Serial.println(filename);
_currentState = _errorState;
}
case _saveState:
//Serial.println("Current State: _saveState");
_currentState = _saveState;
dataFile.flush();
dataFile.close();
_currentState = _readState;
break;
case _errorState:
_currentState = _errorState;
// Red LED HIGH
}
}
// Return to the begining
}
你应该看看这里的其他答案,比如 this one。粗略地说,您必须将机器及其状态分开。嵌入式代码通常需要紧凑和高效,状态机可以使用转换表来实现。