In the below code, the "if" that includes the pulseIns seems to have some kind of a problem that causes the sendIsTouched() call never to be reached if the delayMicroseconds() call is not included in the main loop. Can anybody think of any reason that simply adding a delay could affect the code in that "if" region.
#include <ArduinoUniqueID.h>
#define timeGap 1500UL // millis
unsigned long prevTime = 0;
bool doLameDetection = false;
unsigned long lameDetectionDisableTime;
int latestLameFreq = 0;
int freqDiff = 0;
String bleDeviceAddress = "ns";
char *uniqueid;
#define a0pin A0
#define d2pin 2
short analogVal0 = 0;
int touchmode = 0;
unsigned long touchmillistart;
int loopssincecondition = 0;
int otherSwordFreq = 0;
int swordFreq = 0;
#define MARGIN 400
void setup() {
Serial.begin(115200);
uniqueid = (char*) malloc(2 * UniqueIDsize + 1);
for (int i = 0; i < UniqueIDsize; i++) {
sprintf(uniqueid + 2*i, "%02x", UniqueID[i]);
}
uniqueid[2 * UniqueIDsize] = '\0';
if (UniqueID[UniqueIDsize-1] == 0x77) {
otherSwordFreq = 3900; // 3300 under 5V computer power
swordFreq = 4800; // 4600 under 5V computer power
} else {
otherSwordFreq = 4800; // 4600 under 5V computer power
swordFreq = 3900; // 3300 under 5V computer power
}
pinMode(d2pin, INPUT);
}
void sendIsTouched() {
Serial.print("1");
}
void sendTouched() {
Serial.print("2");
}
void sendText(char *s) {
char slen[100];
sprintf(slen, "3%4d%s", strlen(s), s);
Serial.print(slen);
}
void sendInt(int s) {
char buf[20];
sprintf(buf, "%d", s);
sendText(buf);
}
#define NEEDED_PULSES 2
void loop() {
int i;
unsigned long curMillis = millis();
if (prevTime == 0) {
prevTime = curMillis;
doLameDetection = true;
}
if (!doLameDetection) {
unsigned long timeDiff = curMillis - lameDetectionDisableTime;
if (timeDiff > 2000UL) {
doLameDetection = true;
}
}
int phigh=13, plow=13;
if (doLameDetection) {
for(int j = 0; j < NEEDED_PULSES; ++j) {
phigh = pulseIn(d2pin, LOW, 1000);
if (phigh != 0) {
plow = pulseIn(d2pin, HIGH, 1000);
if (plow != 0) {
latestLameFreq = 1000000 / (phigh + plow);
freqDiff = latestLameFreq - otherSwordFreq;
if (freqDiff > -MARGIN && freqDiff < MARGIN) {
if (j == NEEDED_PULSES - 1) {
sendIsTouched();
sendInt(latestLameFreq);
doLameDetection = false;
lameDetectionDisableTime = curMillis;
}
} else {
break;
}
} else {
break;
}
} else {
break;
}
}
}
analogVal0 = analogRead(a0pin);
delayMicroseconds(100);
if (analogVal0 > 590) {
loopssincecondition = 0;
if (touchmode == 0) {
touchmillistart = curMillis;
touchmode = 1;
} else if (touchmode == 1) {
unsigned long diff = curMillis - touchmillistart;
if (diff >= 15UL) {
sendTouched();
sendInt(otherSwordFreq);
touchmode = 2;
}
}
} else {
loopssincecondition++;
if (touchmode > 0) {
if (loopssincecondition > 1000) {
touchmode = 0;
}
}
}
}
It is a DFRobot Beetle BLE (also called dfr0339) which I believe is an UNO. When we ran into the issues, we put in some code that sent various values over the bluetooth connection where we could see them. We noticed that when this debugging code was in that the system worked. When we took the debugging code out, it didn't work. I figured there was nothing special about the debugging code itself and figured it was more likely a timing issue so we added in that 100us delay and that seemed to be enough to also fix the problem. Our system should work fine with that delay in there but I'm just trying to understand why it matters. Thanks for the reply @jremington .
The analogRead would be performed by the ATmega328 portion of the board, but I have no idea how the communications with the WiFi chip are handled. Presumably that is where the problem lies, perhaps with interrupts running in the background.
Since analogVal0 is already available, it is very difficult to imagine how the delay in this segment could affect the execution of the next if statement.
analogVal0 = analogRead(a0pin);
delayMicroseconds(100);
if (analogVal0 > 590)
Post on the DFRobot forum or website. Maybe someone there knows.
Just to be clear, the problem is not in the section that uses analogVal0. The problem seems to be in the prior section before the delay where it is using pulseIn to get the lengths of 2 cycles of the signal on digital pin 2. I guess theoretically it could be the bluetooth send that is failing in that section but I don't believe that to be the case since we haven't seen other cases where bluetooth writes failed. More likely I think it would be some problem with the pulseIn calls themselves.
I had told my son to add this delay in and for some reason he decided to put it here after the analogRead. That may be confusing people here. With or without the delay, the code relating to pin A0 has never been a problem. The code relating to pin 2 seems to be the problem. I thought the delay should probably go at the end. We can move the delay around and see if the location makes a difference
#define MyHW
#ifdef MyHW
# include "sim.hh"
const int UniqueIDsize = 80;
char UniqueID [UniqueIDsize];
#else
# include <ArduinoUniqueID.h>
#endif
#define timeGap 1500UL // millis
unsigned long prevTime = 0;
bool doLameDetection = false;
unsigned long lameDetectionDisableTime;
int latestLameFreq = 0;
int freqDiff = 0;
String bleDeviceAddress = "ns";
char *uniqueid;
#define a0pin A0
#define d2pin 2
short analogVal0 = 0;
int touchmode = 0;
unsigned long touchmillistart;
int loopssincecondition = 0;
int otherSwordFreq = 0;
int swordFreq = 0;
#define MARGIN 400
void setup() {
Serial.begin(115200);
uniqueid = (char*) malloc(2 * UniqueIDsize + 1);
for (int i = 0; i < UniqueIDsize; i++) {
sprintf(uniqueid + 2*i, "%02x", UniqueID[i]);
}
uniqueid[2 * UniqueIDsize] = '\0';
if (UniqueID[UniqueIDsize-1] == 0x77) {
otherSwordFreq = 3900; // 3300 under 5V computer power
swordFreq = 4800; // 4600 under 5V computer power
} else {
otherSwordFreq = 4800; // 4600 under 5V computer power
swordFreq = 3900; // 3300 under 5V computer power
}
pinMode(d2pin, INPUT);
}
void sendIsTouched() {
Serial.println ("sendIsTouched");
}
void sendTouched() {
Serial.println ("sendTouched");
}
void sendText(char *s) {
char slen[100];
sprintf(slen, "sendText: %d %s", strlen(s), s);
Serial.println(slen);
}
void sendInt(int s) {
char buf[20];
sprintf(buf, "sendInt: %d\n", s);
sendText(buf);
}
#define NEEDED_PULSES 2
void loop() {
int i;
unsigned long curMillis = millis();
if (prevTime == 0) {
prevTime = curMillis;
doLameDetection = true;
}
if (!doLameDetection) {
unsigned long timeDiff = curMillis - lameDetectionDisableTime;
if (timeDiff > 2000UL) {
doLameDetection = true;
}
}
int phigh=13, plow=13;
if (doLameDetection) {
for(int j = 0; j < NEEDED_PULSES; ++j) {
// Serial.println (j);
phigh = pulseIn(d2pin, LOW, 1000);
if (phigh != 0) {
plow = pulseIn(d2pin, HIGH, 1000);
if (plow != 0) {
latestLameFreq = 1000000 / (phigh + plow);
freqDiff = latestLameFreq - otherSwordFreq;
if (freqDiff > -MARGIN && freqDiff < MARGIN) {
if (j == NEEDED_PULSES - 1) {
sendIsTouched();
sendInt(latestLameFreq);
doLameDetection = false;
lameDetectionDisableTime = curMillis;
}
}
else {
break;
}
}
else {
break;
}
}
else {
break;
}
}
}
analogVal0 = analogRead(a0pin);
// delayMicroseconds(100);
if (analogVal0 > 590) {
loopssincecondition = 0;
if (touchmode == 0) {
touchmillistart = curMillis;
touchmode = 1;
}
else if (touchmode == 1) {
unsigned long diff = curMillis - touchmillistart;
if (diff >= 15UL) {
sendTouched();
sendInt(otherSwordFreq);
touchmode = 2;
}
}
}
else {
loopssincecondition++;
if (touchmode > 0) {
if (loopssincecondition > 1000) {
touchmode = 0;
}
}
}
}
looks like the code is attempting to used pulseIn() to measure a distance and is typically written as follows (see Ultrasonic Sensor HC-SR-04 and Arduino)
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance = duration * 0.034 / 2;
It isn't measuring distance. This system is for the sport of fencing. There is a PCB for each fencer that emits either a 4800Hz or 3900Hz signal that travels down the sword to the tip. When the tip touches the metal lame that the other fencer wears, that signal is transmitted down a wire to the other fencer's arduino pin 2. All these wires aren't insulated and run right next to each other so there is crosstalk and they pick up signals from the environment and so we need to make sure that we are receiving the frequency that we know is associated with the other sword so that we can report that we have been touched. The pulseIns are measuring the frequency of the signal that is currently appearing on the lame. I appreciate you actually trying the code out but you shouldn't see the isTouched message unless you put a specific frequency on pin 2. I'm surprised you got those other messages...did you intentionally apply a voltage to A0? If you had an oscilloscope you could use 5000Hz on pin 2 and change the code and then perhaps you could reproduce the issue but that is above and beyond.
You measure the length of the high pulse and the length of the low pulse and then do the math to calculate the corresponding frequency. The line is "latestLameFreq = 1000000 / (phigh + plow);"
Please read what I said again. PulseIn can measure the length of the high part or the low part. I said that I measure the length of the high part and then the low part and then add them before dividing 1000000 by that value to get the frequency. I have verified this code with an oscilloscope so I know this code works in principle. Without the delay that I mentioned, it would appear that some of these pulseIns are either returning the wrong length or not detecting the low or high periods at all within the given timeout. So, again, this code works and gets the right frequency at the right time with the delay in there but doesn't work with the delay removed.
guess i'm having a hard time understanding your code and what's it's intended to do
but since pulseIn() waits for the pulse to either rise or fall depending on it's argument and after being called to measure the HIGH pulse is then called to measure the LOW pulse, i don't see how it can measure the LOW pulse immediately following the HIGH pulse it just measured. so it's probably measuring the LOW pulse after the following HIGH pulse.
so i think the code is recognizing twice the NEEDED_PULSE ... not that
i see how this explains you're problem
could it be the timeouts aren't long enough and that's why the delayMicroseconds() helps shift the starting point
Yes, that was my thought as well. If you look for high then low you won't get the low immediately after the high but probably the one after that. If you really have a consistent signal at a particular frequency then that won't really matter that you are picking the highs and lows from different cycles. The reason needed_pulses is 2 is because if you just do this process of computing the frequency once then I was getting a bunch of spurious cases where it thought it was seeing the frequency but the frequency wasn't being applied at that time. So, I just do it twice in a row and haven't seen any cases where the computed frequency twice in a row was the target frequency and it wasn't really present. From the code, you can see that the two frequencies are 3.6K and 4.8K and if you compute the period for those, they are in the 200-300us range. So, the pulseIn timeout is >3x the period already. I can do an experiment where I increase those timeouts without the delay or lower the timeouts but keep the delay and see what happens.
In terms of overall understanding, the code has to do 2 things. 1) Determine if the voltage on the A0 pin is consistently above some threshold voltage level for more than 15ms. 2) Determine if a signal of a given frequency is present on (digital) pin 2 within 15ms of the start of that signal.
On many pages, they will recommend an approach where you divide time into epochs and then use interrupts to count rising edges on that pin during that epoch but that approaches gives inaccurate results if the signal starts in the middle of an epoch. Even if the signal is consistently there, if the actual epoch length is not a multiple of the signal period then sometimes you'll get one more peak in some epochs. If you make the epochs short enough and then try to find several epochs in a row where the signal is present the whole epoch then you don't get many cycles per epoch and so the above error can be significant. I experimented with this approach quite a bit and it was never very accurate. With the current approach, it should work if there are 4 cycles of the given frequency present in a row and could be recognized in under 1ms.
About the function pulseIn(): the function waits for the previous pulse to end, waits for the new pulse to start before it starts counting.
So the value for the positive half and the value for the negative half come from different periods. Maybe your deadline of 4 cycles of the given frequency is where it goes wrong.
The pulseIn timeout doc says " timeout (optional): the number of microseconds to wait for the pulse to start; default is one second." So, it seems it is the time it waits for it to start, not the time to start plus the length of the pulse. Each of the 4 pulseIn calls will wait up to 1ms and the pulses should be coming in 3x faster than that. In any case, these pulseIn calls are happening in their own loop and the delay in the main loop comes after this loop. So, the mystery remains of how that delay can affect the pulseIn for loop in the next iteration of the main loop.