Hi there. First post...
First of all - thank you to all of you who share your time and expertise - MUCH appreciated.
I'm using an Arduino Grand Central M4 to control and measure the performance of a pendulum clock. I have some experience with coding, but not on Arduinos or to control hardware.
I have the code running, but want to add validation to the beam breaks that are detected by the photo sensor to filter out the occasional spurious beam break signal.
The time between the last beam break and the current one is held in testSwingTime_u. That then gets tested to see if it is within a small tolerance of the expected value. If not then the beam break gets ignored as being a false break.
The problem that I'm having is that the value of testSwingTime_u passed to the validation function
validateBeamBreak(testSwingTime_u, isLeftSwing);
IS NOT what the function then sees and uses
void validateBeamBreak(unsigned long validationTime_u, bool currentSwingIsLeft) {
I've marked it with "[####]" in the example output.
There's a bunch of other code associated with the LCD screen, data logging, GPS, etc that I have removed to simplify things, but the problem remains.
I've got other issues to sort as well - it gets stuck on checking right swings only and it always thinks its on the first, right swing - but I can sort those out myself (probably!) but I'm stuck on this change of the parameter value.
The code is attached together with sample output. I hope I have managed to comply with the guidelines for posting, apologies if I haven't.
Many, many thanks to anyone who can assist.
#include <SPI.h>
//#include <SD.h>
#include <SdFat.h>
#include <Wire.h>
#include <Adafruit_MS8607.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_MotorShield.h>
#include <RTClib.h> // For RTC functionality
#include <Adafruit_RGBLCDShield.h> //Adafruit RGB LCD Shield library v1.2.2
#include <Adafruit_GPS.h>
// Objects
Adafruit_MS8607 ms8607;
//Adafruit_GPS GPS(&GPSSerial); // Connect to the GPS on the hardware port
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *EMagnet1Left = AFMS.getMotor(1);
Adafruit_DCMotor *EMagnet2Right = AFMS.getMotor(2);
//Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
SdFat SD;
File logfile;
// hardware settings
const byte chipSelect = SDCARD_SS_PIN; //pin for interaction with SD card
const byte photoSensorPin = 4;
const byte leftImpulseLED = 5;
const byte rightImpulseLED = 6;
const int flashDuration_ms = 0;
//run time settings
const int pulseLength_ms = 40; // 12 is about the minimum
const int pulseInterval_s = 3;
const int logInterval_s = 6;
const int settlingTime_m = 1; //settling time in minutes
const int runRateInterval_s = 12; //use a multiple of the impulse cycle duration (=2 x pulseInterval_s)
const float swingTolerance = 0.004;
// elapsed time variables
float elapsedTime_s;
int elapsedDays_d;
int elapsedHours_h;
int elapsedMinutes_m;
float elapsedSeconds_s;
//timing variables
//GPS variables
unsigned long gpsDate = 0;
unsigned long gpsTime = 0;
unsigned long clockTime = 0;
unsigned long breakTime_u;
unsigned long runStartTime_u = 0;
//single swing variables
unsigned long singleSwingStartTime_u = 0;
unsigned long singleSwingEndTime_u = 0;
unsigned long singleSwingTime_u;
//window swing variables (logging window)
unsigned long windowStartTime_u = 0;
unsigned long windowEndTime_u = 0;
unsigned long windowTime_u = 0;
//amplitude variables
unsigned long amplitudeStartTime_u = 0;
unsigned long amplitudeEndTime_u = 0;
unsigned long amplitudeTime_u = 0;
unsigned long amplitudeSum_u = 0;
bool amplitudeMeasurementStarted = false;
bool beamUnbroken = false;
//run rate variables
unsigned long runRateStart_u = 0;
unsigned long runRateEnd_u = 0;
unsigned long runRate_u = 0;
// Left and right swing tracking variables
bool isLeftSwing = true; //start from the left; after the first beam break, it's on a right swing
float lastLeftSwingTime_u = 0;
float lastRightSwingTime_u = 0;
float previousLeftSwingTime_u = 0;
float previousRightSwingTime_u = 0;
// beat error variables
float leftSwingTimes_u[10]; //array size needs to be greater than pulseInterval
float rightSwingTimes_u[10]; //array size needs to be greater than pulseInterval
int leftSwingIndex = 0;
int rightSwingIndex = 0;
float beatTimeLeft_u = 0;
float beatTimeRight_u = 0;
float beatError_u = 0;
//validation variables
bool validationActive = false;
bool breakIsValid = true;
unsigned long testSwingTime_u = 0;
bool lastPinState = LOW;
bool currentPinState = LOW;
//misc variables
int filenameHundreds;
int filenameTens;
int displayNum = 0;
unsigned long breakCount = 0;
int pulseBreakCount = 0;
volatile bool beamBroken = false;
bool pulseLeft = false;
void setup() {
// connect at 115200 so we can read the GPS fast enough and echo without
// dropping chars also spit it out
Serial.begin(115200);
/*while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}*/
delay(2000);
Serial.println();
Serial.println(F("### Starting setup ###"));
//initializeLCD();
pinMode(photoSensorPin, INPUT_PULLUP); // Initialize optical sensor with interrupt
attachInterrupt(digitalPinToInterrupt(photoSensorPin), beamBreak, FALLING);
pinMode(leftImpulseLED, OUTPUT);
pinMode(rightImpulseLED, OUTPUT);
Serial.println(F(" Optical sensor - initialized"));
//initialize_MS8607_PHT();
initializeMotorShield();
Serial.println(F("### System initialized. Ready for LEFT start ###"));
Serial.println();
}
void loop() {
if (beamBroken) { //check if it is a valid break; only happens on the first pass through loop() after a beam break
beamBroken = false;
checkBreakIsValid();
//Serial.println();
Serial.print("check#2: breakTime_u=");
Serial.print(breakTime_u);
Serial.print(" singleSwingEndTime_u=");
Serial.print(singleSwingEndTime_u);
Serial.print(" singleSwingStartTime_u=");
Serial.print(singleSwingStartTime_u);
Serial.print(" testSwingTime_u=");
Serial.println(testSwingTime_u);
}
if (breakIsValid) { //it is in the "blocked" phase, marked by amplitudeMeasurementStarted = true;
beamUnbroken = false;
breakCount++;
amplitudeMeasurementStarted = true;
Serial.print("chk3: validation: breakTime_u=");
Serial.print(breakTime_u);
Serial.print(" singleSwingEndTime_u=");
Serial.print(singleSwingEndTime_u);
Serial.print(" singleSwingStartTime_u=");
Serial.print(singleSwingStartTime_u);
Serial.print(" testSwingTime_u=");
Serial.println(testSwingTime_u);
singleSwingStartTime_u = singleSwingEndTime_u;
singleSwingEndTime_u = breakTime_u;
amplitudeStartTime_u = breakTime_u;
Serial.print("chk4: validation: breakTime_u=");
Serial.print(breakTime_u);
Serial.print(" singleSwingEndTime_u=");
Serial.print(singleSwingEndTime_u);
Serial.print(" singleSwingStartTime_u=");
Serial.print(singleSwingStartTime_u);
Serial.print(" testSwingTime_u=");
Serial.println(testSwingTime_u);
if (breakCount == 1) { //first beam break of the run - do some initializing
runStartTime_u = breakTime_u;
singleSwingEndTime_u = breakTime_u;
windowStartTime_u = breakTime_u;
}
}
if (amplitudeMeasurementStarted) { // valid break and in the "blocked" period between break and unbreak
// watch for the end of the blocked period
lastPinState = currentPinState;
currentPinState = digitalRead(photoSensorPin);
if ((lastPinState == LOW) && (currentPinState == HIGH)) { //reached the end of the "blocked" phase
amplitudeEndTime_u = micros();
beamUnbroken = true;
amplitudeMeasurementStarted = false;
} //end of if ((lastPinState == LOW) && (currentPinState == HIGH)) { //reached the end of the "blocked" phase
if (beamUnbroken == true) { //only happens on the first pass through loop() after beam unbreak
beamUnbroken = false;
beamBroken = false;
pulseElectromagnets();
if (singleSwingEndTime_u > singleSwingStartTime_u) { // calculate singleSwingTime_u
singleSwingTime_u = singleSwingEndTime_u - singleSwingStartTime_u;
} else {
singleSwingTime_u = int(float(singleSwingEndTime_u) + 4294967295UL - float(singleSwingStartTime_u));
}
Serial.print("check#5: ");
Serial.print(" breakTime_u=");
Serial.print(breakTime_u);
Serial.print(" singleSwingEndTime_u=");
Serial.print(singleSwingEndTime_u);
Serial.print(" singleSwingStartTime_u=");
Serial.print(singleSwingStartTime_u);
Serial.print(" singleSwingTime_u=");
Serial.print(singleSwingTime_u);
if (isLeftSwing) { // Track left swing durations
// Store the previous left swing time before updating
if (lastLeftSwingTime_u > 0) {
previousLeftSwingTime_u = lastLeftSwingTime_u;
}
lastLeftSwingTime_u = singleSwingTime_u;
} else { // Track right swing durations
// Store the previous right swing time before updating
if (lastRightSwingTime_u > 0) {
previousRightSwingTime_u = lastRightSwingTime_u;
}
lastRightSwingTime_u = singleSwingTime_u;
}
isLeftSwing = !isLeftSwing;
} //end of if (beamUnbroken == true) { //only happens on the first pass through loop() after beam unbreak
} // end of if (amplitudeMeasurementStarted) { // valid break and in the "blocked" period between break and unbreak
if (!validationActive && ((micros() - runStartTime_u) >= 10000000UL)) { // Enable validation 10 seconds after first beam break
validationActive = true;
Serial.println(F("*** Beam break validation activated ***"));
}
amplitudeMeasurementStarted = false;
breakIsValid = false;
} // end of void loop()
void beamBreak() {
breakTime_u = micros();
beamBroken = true;
}
void checkBreakIsValid() {
if (validationActive) {
testSwingTime_u = breakTime_u - singleSwingEndTime_u; //current beam break minus previous swing end
Serial.println();
Serial.print("check#1: breakTime_u=");
Serial.print(breakTime_u);
Serial.print(" singleSwingEndTime_u=");
Serial.print(singleSwingEndTime_u);
Serial.print(" singleSwingStartTime_u=");
Serial.print(singleSwingStartTime_u);
Serial.print(" testSwingTime_u=");
Serial.println(testSwingTime_u);
validateBeamBreak(testSwingTime_u, isLeftSwing);
} else {
breakIsValid = true;
}
}
void validateBeamBreak(unsigned long validationTime_u, bool currentSwingIsLeft) {
Serial.print(F(" in VBB: validationTime_u="));
Serial.println(validationTime_u, 6);
breakIsValid = true; //start with true; change to false if invalid
if (currentSwingIsLeft) {
float leftTolerance_u = previousLeftSwingTime_u * swingTolerance;
Serial.print(F(" check6Left: validationTime_u="));
Serial.print(validationTime_u, 6);
// CLAUDE FIX: Only validate if we have a previous left swing time to compare against
if (previousLeftSwingTime_u > 0) {
if ((validationTime_u < (previousLeftSwingTime_u - leftTolerance_u)) || (validationTime_u > (previousLeftSwingTime_u + leftTolerance_u))) {
breakIsValid = false;
Serial.print(F(" Left swing rejected: "));
} else {
Serial.print(F(" Left swing OK: "));
}
Serial.print(F("validationTime_u="));
Serial.print(validationTime_u, 6);
Serial.print(F(", expected="));
Serial.print(previousLeftSwingTime_u, 6);
Serial.print(F(", difference="));
Serial.print((validationTime_u - previousLeftSwingTime_u) / previousLeftSwingTime_u * 100, 2);
Serial.println(F("%"));
} else {
// No previous left swing time available, accept the swing
Serial.print(F(" Left swing OK (no previous): "));
Serial.print(F("validationTime_u="));
Serial.print(validationTime_u, 6);
Serial.println(F(", expected=N/A (first left swing)"));
}
} else {
float rightTolerance_u = previousRightSwingTime_u * swingTolerance;
Serial.print(F(" check6Right: validationTime_u="));
Serial.print(validationTime_u, 6);
if (previousRightSwingTime_u > 0) {
if (validationTime_u < (previousRightSwingTime_u - rightTolerance_u) || validationTime_u > (previousRightSwingTime_u + rightTolerance_u)) {
breakIsValid = false;
Serial.print(F(" Right swing rejected: "));
} else {
Serial.print(F(" Right swing OK: "));
}
Serial.print(F("validationTime_u="));
Serial.print(validationTime_u, 6);
Serial.print(F(", expected="));
Serial.print(previousRightSwingTime_u, 6);
Serial.print(F(", difference="));
Serial.print((validationTime_u - previousRightSwingTime_u) / previousRightSwingTime_u * 100, 2);
Serial.println(F("%"));
} else {
// No previous right swing time available, accept the swing
Serial.print(F(" Right swing OK (no previous): "));
Serial.print(F("validationTime_u="));
Serial.print(validationTime_u, 6);
Serial.println(F(", expected=N/A (first right swing)"));
}
}
}
void pulseElectromagnets() {
pulseBreakCount++;
// Handle electromagnet pulses
if (pulseBreakCount == pulseInterval_s) {
if (pulseLeft == true) {
impulse_1_left();
} else {
impulse_2_right();
}
pulseLeft = !pulseLeft;
pulseBreakCount = 0;
}
} //end of pulseElectromagnets()
// Electromagnet functions
void impulse_1_left() {
digitalWrite(leftImpulseLED, HIGH);
EMagnet1Left->run(FORWARD);
EMagnet1Left->setSpeed(255);
delay(pulseLength_ms * 1.5);
EMagnet1Left->run(RELEASE);
delay(flashDuration_ms);
digitalWrite(leftImpulseLED, LOW);
}
void impulse_2_right() {
digitalWrite(rightImpulseLED, HIGH);
EMagnet2Right->run(FORWARD);
EMagnet2Right->setSpeed(255);
delay(pulseLength_ms / 1.6);
EMagnet2Right->run(RELEASE);
delay(flashDuration_ms);
digitalWrite(rightImpulseLED, LOW);
}
void initializeMotorShield() {
Serial.print(" Initializing Motor Shield");
if (!AFMS.begin()) {
Serial.println(F(" - Could not find Motor Shield"));
} else {
Serial.println(F(" - Motor Shield initialized successfully"));
}
EMagnet1Left->setSpeed(255);
EMagnet1Left->run(FORWARD);
EMagnet1Left->run(RELEASE);
EMagnet2Right->setSpeed(255);
EMagnet2Right->run(FORWARD);
EMagnet2Right->run(RELEASE);
}
#########################################
EXAMPLE OUTPUT (partial)
#########################################
*** Beam break validation activated ***
check#1: breakTime_u=12005499 singleSwingEndTime_u=11005106 singleSwingStartTime_u=10005547 testSwingTime_u=1000393 [####]
in VBB: validationTime_u=33235241 [####]
check6Right: validationTime_u=33235241 Right swing OK (no previous): validationTime_u=33235241, expected=N/A (first right swing)
check#2: breakTime_u=12005499 singleSwingEndTime_u=11005106 singleSwingStartTime_u=10005547 testSwingTime_u=1000393
chk3: validation: breakTime_u=12005499 singleSwingEndTime_u=11005106 singleSwingStartTime_u=10005547 testSwingTime_u=1000393
chk4: validation: breakTime_u=12005499 singleSwingEndTime_u=12005499 singleSwingStartTime_u=11005106 testSwingTime_u=1000393