我一直在做一个关于Arduino的小项目,我需要一些帮助,使代码更高效/更干净。我真的不喜欢这三个for循环,我觉得我可以以某种方式压缩它们。
它的工作原理:
#include <SoftwareSerial.h>
#include "Timer.h"
SoftwareSerial mySerial(10, 11); // RX, TX
Timer t;
// Pinbelegung
int keyPin = 13;
int detectionPin = 8;
int powerPin = 9;
int doorPin = 4;
// Wert von detectionPin Eingang
int detectionPinRead;
// Serielle Daten
String serialData;
// Rausgefilterte MAC Addresse
String mac;
// Zugangsarrays
const char* whitelist[2] = {"+INQ:BCF5:AC:74DDB7", "+INQ:1068:3F:E1081F"};
int time[2] = {0, 0};
boolean entered[2] = {false, false};
// Zeit seit dem Start
unsigned long currentTime;
// Zugangsbedingung
boolean detected;
boolean retrigger;
void setup() {
Serial.begin(38400);
while (!Serial) { ; }
mySerial.begin(38400);
pinMode(keyPin, OUTPUT);
pinMode(detectionPin, INPUT);
pinMode(powerPin, OUTPUT);
pinMode(doorPin, OUTPUT);
// Versetze Bluetoothmodul in AT Modus
digitalWrite(keyPin, HIGH);
delay(500);
digitalWrite(powerPin, HIGH);
delay(500);
mySerial.println("AT+INIT");
delay(500);
mySerial.println("AT+INQM=1,0,48");
delay(500);
mySerial.println("AT+INQ");
// Ein Scan dauert ca. 62 Sekunden. Starte alle 63 Sekunden einen neuen Scan
t.every(63000, startInq);
}
void loop() {
t.update();
currentTime = millis() / 1000;
//Wenn Serielle Daten verfügbar sind, lese so lange bis end of line
while (mySerial.available() > 0) {
char received = mySerial.read();
serialData += received;
if (received == '\n') {
mac = getToken(serialData, ',', 0);
// Debug
Serial.println(mac);
Serial.println(currentTime);
Serial.println(time[0]);
Serial.println(time[1]);
// Lese den Eingang aus. Wenn HIGH und retrigger ist verhindert, setze Zugangsbedingung
detectionPinRead = digitalRead(detectionPin);
if (detectionPinRead == HIGH && !retrigger) {
for (int i = 0; i < 2; i++) {
if (mac == whitelist[i]) {
detected = true;
retrigger = true;
break;
}
}
}
// Überprüfe ob MAC seit mindestens 10 Sekunden nicht mehr in Rechweite ist
for (int i = 0; i < 2; i++) {
if(mac == whitelist[i] && currentTime - time[i] >= 10) {
entered[i] = false;
retrigger = false;
break;
}
}
// Öffne Tür sobald Zugansbedingungen erfüllt sind
for (int i = 0; i < 2; i++) {
if (mac == whitelist[i]) {
time[i] = currentTime;
if (!entered[i] && detected) {
entered[i] = true;
openDoor();
break;
}
}
}
serialData = "\0";
}
}
}
String getToken(String data, char separator, int index) {
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for(int i = 0; i <= maxIndex && found <= index; i++) {
if(data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
void openDoor() {
digitalWrite(doorPin, HIGH);
delay(500);
digitalWrite(doorPin, LOW);
detected = false;
}
void startInq() {
mySerial.println("AT+INQ");
}发布于 2014-08-17 19:44:17
为了使这段代码更加清晰,您肯定可以做一些事情。
中的全局变量
拥有依赖于全局变量的例程使得理解逻辑变得更加困难,并引入了许多错误的机会。在实用的地方消除全局变量总是一个好主意,无论是为桌面机器编程还是为嵌入式系统编程。对于全局变量(如currentTime ),请考虑将它们封装在对象中,以便于区分读取访问和更新。
从文本描述中提取逻辑,我们可以用psuedocode来表示:
for each detected mac:
if (isWhitelisted(mac) && sensor && isEligible(mac))
openDoor()剩下的就是保持这些变量的更新和正确。sensor变量只是您的detectionPinRead == HIGH语句,所以其中一个非常简单。检查白名单是您在每个循环中已经做的事情,所以这也非常简单。唯一剩下的就是确定给定的mac设备是否有资格打开门。一个设备是合格的,如果它是新的,或如果它已经超出范围至少10秒。在后面的部分中,我将展示一种方法。
白名单中的设备具有与每个设备相关联的数据和逻辑。这强烈地暗示了一个物体。我提出这样的建议:
class AuthorizedBT
{
public:
AuthorizedBT(const char *mac) : MAC(mac), lasttime(0), outside(true) {}
bool matches(const String& otherMAC) const { return MAC == otherMAC; }
bool isEligible(int currTime) const {
return outside || currTime >= (lasttime + 10); }
void markSeen(int currTime) { lasttime = currTime; }
void markInside() { outside = false; }
private:
const char* MAC;
int lasttime;
bool outside;
};您的白名单现在可以由对象构建:
AuthorizedBT whitelist[2] = {"one", "two"};分离
您的循环的简化版本可以重写为将I/O与逻辑分开:
void loop() {
// update time, fetch mac from serial port
update(currentTime, mac, HIGH == digitalRead(detectionPin));
}现在,您的代码中执行串行I/O、更新时间和读取端口的活动部分与代码的逻辑部分(现在放置在一个名为update的例程中)完全分开:
bool update(int currentTime, const char *mac, bool sensor)
{
bool result = false;
for (int i=0; i < 2; ++i) {
if (whitelist[i].matches(mac)) {
if (sensor && whitelist[i].isEligible(currentTime)) {
openDoor();
whitelist[i].markInside();
result = true;
}
whitelist[i].markSeen(currentTime);
}
}
return result;
}这就消除了全局变量retrigger和detected。更好的是,您可以从这个例程中删除对openDoor的调用,并使用返回值来确定何时打开门。这导致了更干净的分离。此外,如果您能够确保currentTime始终至少是10,则可以消除outside数据成员和对它的所有引用。
有了I/O与逻辑的清晰分离,就有可能将逻辑与实际机器分开测试。这是非常有益的,特别是在开发一个原型的时候。
int main()
{
assert(false == update(1, "foo", true)); // not whitelisted; no action
assert(false == update(20, "one", false)); // didn't trigger sensor; no action
assert(true == update(21, "one", true)); // "one" is now inside
assert(false == update(22, "one", true)); // "one" is already inside; no action
assert(false == update(31, "one", true)); // "one" is already inside; no action
assert(true == update(41, "one", true)); // "one" was out of range for 10 sec; open door
assert(true == update(42, "two", true)); // "two" is now inside
assert(false == update(43, "one", true)); // "one" is already inside; no action
assert(false == update(43, "two", true)); // "two" is already inside; no action
}代码当前有一个全局变量currentTime,它被声明为unsigned long,但是值存储在time数组中,每个成员都是int。即使您的编译器认为long和int具有相同的大小,它也肯定不会以相同的方式对待已签名和无符号的版本。
time数组很容易导致与<ctime>头中内置的time函数的冲突。最好是使用一个变量名,这个变量名还不是一个常用的变量或函数名。
bool而不是boolean标准类型是bool,而不是boolean。除非您的C++编译器有什么问题,否则应该使用bool。
代码使用String类型,但不显示实现。如果是std::string类型或其他对象,请注意不要无意中创建临时副本。您可以通过仔细选择参数声明来避免这种情况。例如,而不是
String getToken(String data, char separator, int index)您可以考虑以下形式:
String getToken(const String &data, char separator, int index)代码中有几个数字,比如2和10,它们在特定的上下文中有特定的含义。通过使用命名常量(如NUMBER_OF_WHITELISTED_OBJECTS或MIN_OUT_OF_RANGE_SECONDS ),程序更易于读取和维护。对于常量只对特定对象有意义的情况,请考虑使对象的常量部分。
https://codereview.stackexchange.com/questions/60193
复制相似问题