speech to text throws error Audio Timeout Error: Long duration elapsed without audio. Audio should be sent close to real time - google-cloud-speech

Speech to text throws an error after some time:
Grpc.Core.RpcException: Status(StatusCode=OutOfRange, Detail="Audio
Timeout Error: Long duration elapsed without audio. Audio should be
sent close to real time.")
Another error is:
Grpc.Core.RpcException: Status(StatusCode=Cancelled, Detail="The
operation was cancelled.")
How to avoid these errors? Both the errors are at function:
if (firstMessage == false) {
// var buffer = new byte[32 * 1024];
var buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await outputStream.ReadAsync(
buffer, 0, buffer.Length)) > 0) {
await streamingCall.WriteAsync(
new StreamingRecognizeRequest() {
AudioContent = Google.Protobuf.ByteString
.CopyFrom(buffer, 0, bytesRead),
});
// await Task.Delay(100);
};
}

You must be doing some speech recognition from a streaming input. This error usually occurs when there's a period of silence (e.g 10 seconds or more) in your streaming input, thus, it signals the end of the recognition and cancels the operation. To avoid, make sure to send inputs close to real time.

Related

Clipping sound with opus on Android, sent from IOS

I am recording audio in IOS from audioUnit, encoding the bytes with opus and sending it via UDP to android side. The problem is that the sound is playing a bit clipped. I have also tested the sound by sending the Raw data from IOS to Android and it plays perfect.
My AudioSession code is
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker])
try audioSession.setPreferredIOBufferDuration(0.02)
try audioSession.setActive(true)
My recording callBack code is:
func performRecording(
_ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp: UnsafePointer<AudioTimeStamp>,
inBufNumber: UInt32,
inNumberFrames: UInt32,
ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus
{
var err: OSStatus = noErr
err = AudioUnitRender(audioUnit!, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData)
if let mData = ioData[0].mBuffers.mData {
let ptrData = mData.bindMemory(to: Int16.self, capacity: Int(inNumberFrames))
let bufferPtr = UnsafeBufferPointer(start: ptrData, count: Int(inNumberFrames))
count += 1
addedBuffer += Array(bufferPtr)
if count == 2 {
let _ = TPCircularBufferProduceBytes(&circularBuffer, addedBuffer, UInt32(addedBuffer.count * 2))
count = 0
addedBuffer = []
let buffer = TPCircularBufferTail(&circularBuffer, &availableBytes)
memcpy(&targetBuffer, buffer, Int(min(bytesToCopy, Int(availableBytes))))
TPCircularBufferConsume(&circularBuffer, UInt32(min(bytesToCopy, Int(availableBytes))))
self.audioRecordingDelegate(inTimeStamp.pointee.mSampleTime / Double(16000), targetBuffer)
}
}
return err;
}
Here i am getting inNumberOfFrames almost 341 and i am appending 2 arrays together to get a bigger framesize (needed 640) for Android but i am only encoding 640 by the help of TPCircularBuffer.
func gotSomeAudio(timeStamp: Double, samples: [Int16]) {
samples.count))
let encodedData = opusHelper?.encodeStream(of: samples)
OPUS_SET_BITRATE_REQUEST)
let myData = encodedData!.withUnsafeBufferPointer {
Data(buffer: $0)
}
var protoModel = ProtoModel()
seqNumber += 1
protoModel.sequenceNumber = seqNumber
protoModel.timeStamp = Date().currentTimeInMillis()
protoModel.payload = myData
DispatchQueue.global().async {
do {
try self.tcpClient?.send(data: protoModel)
} catch {
print(error.localizedDescription)
}
}
let diff = CFAbsoluteTimeGetCurrent() - start
print("Time diff is \(diff)")
}
In the above code i am opus encoding 640 frameSize and adding it to ProtoBuf payload and Sending it via UDP.
On Android side i am parsing the Protobuf and decoding the 640 framesize and playing it with AudioTrack.There is no problem with android side as i have recorded and played sound just by using Android but the problem comes when i record sound via IOS and play through Android Side.
Please don't suggest to increase the frameSize by setting Preferred IO Buffer Duration. I want to do it without changing this.
https://stackoverflow.com/a/57873492/12020007 It was helpful.
https://stackoverflow.com/a/58947295/12020007
I have updated my code according to your suggestion, removed the delegate and array concatenation but there is still clipping on android side. I have also calculated the time it takes to encode bytes that is approx 2-3 ms.
Updated callback code is
var err: OSStatus = noErr
// we are calling AudioUnitRender on the input bus of AURemoteIO
// this will store the audio data captured by the microphone in ioData
err = AudioUnitRender(audioUnit!, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData)
if let mData = ioData[0].mBuffers.mData {
_ = TPCircularBufferProduceBytes(&circularBuffer, mData, inNumberFrames * 2)
print("mDataByteSize: \(ioData[0].mBuffers.mDataByteSize)")
count += 1
if count == 2 {
count = 0
let buffer = TPCircularBufferTail(&circularBuffer, &availableBytes)
memcpy(&targetBuffer, buffer, min(bytesToCopy, Int(availableBytes)))
TPCircularBufferConsume(&circularBuffer, UInt32(min(bytesToCopy, Int(availableBytes))))
let encodedData = opusHelper?.encodeStream(of: targetBuffer)
let myData = encodedData!.withUnsafeBufferPointer {
Data(buffer: $0)
}
var protoModel = ProtoModel()
seqNumber += 1
protoModel.sequenceNumber = seqNumber
protoModel.timeStamp = Date().currentTimeInMillis()
protoModel.payload = myData
do {
try self.udpClient?.send(data: protoModel)
} catch {
print(error.localizedDescription)
}
}
}
return err;
Your code is doing Swift memory allocation (Array concatenation) and Swift method calls (your recording delegate) inside the audio callback. Apple (in a WWDC session on Audio) recommends not doing any memory allocation or method calls inside the real-time audio callback context (especially when requesting short Preferred IO Buffer Durations). Stick to C function calls, such as memcpy and TPCircularBuffer.
Added: Also, don't discard samples. If you get 680 samples, but only need 640 for a packet, keep the 40 "left over" samples and use them appended in front of a later packet. The circular buffer will save them for you. Rinse and repeat. Send all the samples you get from the audio callback when you've accumulated enough for a packet, or yet another packet when you end up accumulating 1280 (2*640) or more.

Dart: How I can get length of Stream?

I want to get length of BehaviorSubject's stream, but I can't get it.
test('get stream length', ()async{
BehaviorSubject<int> subject = new BehaviorSubject(seedValue: 0);
var act = await subject.stream.length;
expect(act, 1);
});
How I can get this length?
The length of a stream can only be known after it is closed. As long as it is not closed it's always possible that another event will be added.
https://api.dartlang.org/stable/2.1.1/dart-async/Stream/length.html
length property
Future<int> length
The number of elements in this
stream.
Waits for all elements of this stream. When this stream ends, the
returned future is completed with the number of elements.
If this stream emits an error, the returned future is completed with
that error, and processing stops.
This operation listens to this stream, and a non-broadcast stream
cannot be reused after finding its length.
test('get stream length', ()async{
BehaviorSubject<int> subject = new BehaviorSubject(seedValue: 0);
var actFuture = await subject.stream.length;
await subject.close();
expect(actFuture, completion(equals(1));
});

The sound muted after playing audio with Audio Queue on iOS for a while

I am coding a real time audio playback program on iOS.
It receives audio RTP packages from the peer, and put it into audio queue to play.
When start playing, the sound is OK. But after 1 or 2 minutes, the sound muted, and there is no error reported from AudioQueue API. The callback function continues being called normally, nothing abnormal.
But it just muted.
My callback function:
1: Loop until there is enough data can be copied to audio queue buffer
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
2: Copy to AudioQueue Buffer, and enqueue it. This callback function keeps running normally and no error.
//copy to audio queue buffer
read_bytes = kAudioQueueBufferLength;
g_audio_playback_buf.Read((unsigned char *)inBuffer->mAudioData, read_bytes);
WriteLog(LOG_PHONE_DEBUG, "AudioQueueBuffer(Play): copy [%d] bytes to AudioQueue buffer! Total len = %d", read_bytes, read_bytes_enabled);
inBuffer->mAudioDataByteSize = read_bytes;
UInt32 nPackets = read_bytes / g_audio_periodsize; // mono
inBuffer->mPacketDescriptionCount = nPackets;
// re-enqueue this buffer
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
The problem has been resolved.
The key point is, you can not let the audio queue buffer waits, you must keep feeding it, or it might be muted. If you don't have enough data, fill it with blank data.
so the following code should be changed:
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
changed to this:
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled < kAudioQueueBufferLength)
{
memset(inBuffer->mAudioData, 0x00, kAudioQueueBufferLength);
}
else
{
inBuffer->mAudioDataByteSize = kAudioQueueBufferLength;
}
...
You can let the AudioQueue wait if you use AudioQueuePause.
In this exemple, in Swift 5, I use a generic queue. When this queue is empty, as you did, I fill my buffer with empty data in callback and call AudioQueuePause. It's important to note that all of AudioQueueBuffer send to AudioQueueRef with AudioQueueEnqueueBuffer before call AudioQueuePause are played.
Create an userData class to send everything you need to your callback :
class UserData {
let dataQueue = Queue<Data>()
let semaphore = DispatchSemaphore(value: 1)
}
private var inQueue: AudioQueueRef!
private var userData = UserData()
Give an instance of this class when you create your AudioQueue and start it :
AudioQueueNewOutput(&inFormat, audioQueueOutputCallback, &userData, nil, nil, 0, &inQueue)
AudioQueueStart(inQueue, nil)
Generate all your buffers and don't enqueue them directly : call your callback function :
for _ in 0...2 {
var bufferRef: AudioQueueBufferRef!
AudioQueueAllocateBuffer(inQueue, 320, &bufferRef)
audioQueueOutputCallback(&userData, inQueue, bufferRef)
}
When you receive audio data, you can call a method who enqueue your data and let it wait for callback function get it :
func audioReceived(_ audio: Data) {
let dataQueue = userData.dataQueue
let semaphore = userData.semaphore
semaphore.wait()
dataQueue.enqueue(audio)
semaphore.signal()
// Start AudioQueue every time, if it's already started this call do nothing
AudioQueueStart(inQueue, nil)
}
Finally you can implement a callback function like this :
private let audioQueueOutputCallback: AudioQueueOutputCallback = { (inUserData, inAQ, inBuffer) in
// Get data from UnsageMutableRawPointer
let userData: UserData = (inUserData!.bindMemory(to: UserData.self, capacity: 1).pointee)
let queue = userData.dataQueue
let semaphore = userData.semaphore
// bind UnsafeMutableRawPointer to UnsafeMutablePointer<UInt8> for data copy
let audioBuffer = inBuffer.pointee.mAudioData.bindMemory(to: UInt8.self, capacity: 320)
if queue.isEmpty {
print("Queue is empty: pause")
AudioQueuePause(inAQ)
audioBuffer.assign(repeating: 0, count: 320)
inBuffer.pointee.mAudioDataByteSize = 320
} else {
semaphore.wait()
if let data = queue.dequeue() {
data.copyBytes(to: audioBuffer, count: data.count)
inBuffer.pointee.mAudioDataByteSize = data.count
} else {
print("Error: queue is empty")
semaphore.signal()
return
}
semaphore.signal()
}
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
In my case I use 320 bytes buffer for 20ms of PCM data 16bits, 8kHz, mono.
This solution is more complexe but better than a pseudo infinite loop with empty audio data for your CPU. Apple is very punitive with greedy apps ;)
I hope this solution will help.

Audioqueue callback not being called

So, basically I want to play some audio files (mp3 and caf mostly). But the callback never gets called. Only when I call them to prime the queue.
Here's my data struct:
struct AQPlayerState
{
CAStreamBasicDescription mDataFormat;
AudioQueueRef mQueue;
AudioQueueBufferRef mBuffers[kBufferNum];
AudioFileID mAudioFile;
UInt32 bufferByteSize;
SInt64 mCurrentPacket;
UInt32 mNumPacketsToRead;
AudioStreamPacketDescription *mPacketDescs;
bool mIsRunning;
};
Here's my callback function:
static void HandleOutputBuffer (void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
{
NSLog(#"HandleOutput");
AQPlayerState *pAqData = (AQPlayerState *) aqData;
if (pAqData->mIsRunning == false) return;
UInt32 numBytesReadFromFile;
UInt32 numPackets = pAqData->mNumPacketsToRead;
AudioFileReadPackets (pAqData->mAudioFile,
false,
&numBytesReadFromFile,
pAqData->mPacketDescs,
pAqData->mCurrentPacket,
&numPackets,
inBuffer->mAudioData);
if (numPackets > 0) {
inBuffer->mAudioDataByteSize = numBytesReadFromFile;
AudioQueueEnqueueBuffer (pAqData->mQueue,
inBuffer,
(pAqData->mPacketDescs ? numPackets : 0),
pAqData->mPacketDescs);
pAqData->mCurrentPacket += numPackets;
} else {
// AudioQueueStop(pAqData->mQueue, false);
// AudioQueueDispose(pAqData->mQueue, true);
// AudioFileClose (pAqData->mAudioFile);
// free(pAqData->mPacketDescs);
// free(pAqData->mFloatBuffer);
pAqData->mIsRunning = false;
}
}
And here's my method:
- (void)playFile
{
AQPlayerState aqData;
// get the source file
NSString *p = [[NSBundle mainBundle] pathForResource:#"1_Female" ofType:#"mp3"];
NSURL *url2 = [NSURL fileURLWithPath:p];
CFURLRef srcFile = (__bridge CFURLRef)url2;
OSStatus result = AudioFileOpenURL(srcFile, 0x1/*fsRdPerm*/, 0/*inFileTypeHint*/, &aqData.mAudioFile);
CFRelease (srcFile);
CheckError(result, "Error opinning sound file");
UInt32 size = sizeof(aqData.mDataFormat);
CheckError(AudioFileGetProperty(aqData.mAudioFile, kAudioFilePropertyDataFormat, &size, &aqData.mDataFormat),
"Error getting file's data format");
CheckError(AudioQueueNewOutput(&aqData.mDataFormat, HandleOutputBuffer, &aqData, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aqData.mQueue),
"Error AudioQueueNewOutPut");
// we need to calculate how many packets we read at a time and how big a buffer we need
// we base this on the size of the packets in the file and an approximate duration for each buffer
{
bool isFormatVBR = (aqData.mDataFormat.mBytesPerPacket == 0 || aqData.mDataFormat.mFramesPerPacket == 0);
// first check to see what the max size of a packet is - if it is bigger
// than our allocation default size, that needs to become larger
UInt32 maxPacketSize;
size = sizeof(maxPacketSize);
CheckError(AudioFileGetProperty(aqData.mAudioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize),
"Error getting max packet size");
// adjust buffer size to represent about a second of audio based on this format
CalculateBytesForTime(aqData.mDataFormat, maxPacketSize, 1.0/*seconds*/, &aqData.bufferByteSize, &aqData.mNumPacketsToRead);
if (isFormatVBR) {
aqData.mPacketDescs = new AudioStreamPacketDescription [aqData.mNumPacketsToRead];
} else {
aqData.mPacketDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
}
printf ("Buffer Byte Size: %d, Num Packets to Read: %d\n", (int)aqData.bufferByteSize, (int)aqData.mNumPacketsToRead);
}
// if the file has a magic cookie, we should get it and set it on the AQ
size = sizeof(UInt32);
result = AudioFileGetPropertyInfo(aqData.mAudioFile, kAudioFilePropertyMagicCookieData, &size, NULL);
if (!result && size) {
char* cookie = new char [size];
CheckError(AudioFileGetProperty(aqData.mAudioFile, kAudioFilePropertyMagicCookieData, &size, cookie),
"Error getting cookie from file");
CheckError(AudioQueueSetProperty(aqData.mQueue, kAudioQueueProperty_MagicCookie, cookie, size),
"Error setting cookie to file");
delete[] cookie;
}
aqData.mCurrentPacket = 0;
for (int i = 0; i < kBufferNum; ++i) {
CheckError(AudioQueueAllocateBuffer (aqData.mQueue,
aqData.bufferByteSize,
&aqData.mBuffers[i]),
"Error AudioQueueAllocateBuffer");
HandleOutputBuffer (&aqData,
aqData.mQueue,
aqData.mBuffers[i]);
}
// set queue's gain
Float32 gain = 1.0;
CheckError(AudioQueueSetParameter (aqData.mQueue,
kAudioQueueParam_Volume,
gain),
"Error AudioQueueSetParameter");
aqData.mIsRunning = true;
CheckError(AudioQueueStart(aqData.mQueue,
NULL),
"Error AudioQueueStart");
}
And the output when I press play:
Buffer Byte Size: 40310, Num Packets to Read: 38
HandleOutput start
HandleOutput start
HandleOutput start
I tryed replacing CFRunLoopGetCurrent() with CFRunLoopGetMain() and CFRunLoopCommonModes with CFRunLoopDefaultMode, but nothing.
Shouldn't the primed buffers start playing right away I start the queue?
When I start the queue, no callbacks are bang fired.
What am I doing wrong? Thanks for any ideas
What you are basically trying to do here is a basic example of audio playback using Audio Queues. Without looking at your code in detail to see what's missing (that could take a while) i'd rather recommend to you to follow the steps in this basic sample code that does exactly what you're doing (without the extras that aren't really relevant.. for example why are you trying to add audio gain?)
Somewhere else you were trying to play audio using audio units. Audio units are more complex than basic audio queue playback, and I wouldn't attempt them before being very comfortable with audio queues. But you can look at this example project for a basic example of audio queues.
In general when it comes to Core Audio programming in iOS, it's best you take your time with the basic examples and build your way up.. the problem with a lot of tutorials online is that they add extra stuff and often mix it with obj-c code.. when Core Audio is purely C code (ie the extra stuff won't add anything to the learning process). I strongly recommend you go over the book Learning Core Audio if you haven't already. All the sample code is available online, but you can also clone it from this repo for convenience. That's how I learned core audio. It takes time :)

Arduino 'time out' function using a millis timer

I've not been programming for long and I just want to expand from electronic engineering with an Arduino UNO board.
I've started a new project based on the Secret Knock Detecting Door Lock by Steve Hoefer on Grathio and I'd like to implement the following:
(http://grathio.com/2009/11/secret_knock_detecting_door_lock/)
(http://grathio.com/assets/secret_knock_detector.pde)
Implementation
If the global value equals 0 and the valid knock patter is true then flash a yellow LED 4 times using millis rather than delay so that it can still 'listen'.
If another valid knock pattern is not heard within 6 seconds it will time out and reset global to 0 so that it can acknowledge the initial true pattern and flash the yellow LED.
If another valid knock pattern is heard withing 6 seconds, increment a counter.
If the counter equals 1, wait for another valid knock pattern and if true within 6 seconds, increment the counter again and don't flash the yellow LED.
Otherwise, time out and reset all values.
And so on until if the counter is greater than or equal to 4 trigger the master LED array.
Once is gets to 4 successful knocks, I'd like it to trigger the master LED array I've built.
Problems
This project was inspired by the test panels used on passenger airplanes. I've seen them a lot and thought it would be a good place to start and learn about timing.
There are a few problems as I don't wish to reset millis() every time and I'm using a button rather than the boolean within the knock detection script so I don't get lost in the code.
I understand this won't respond 50 seconds later and it's a beginners mistake but proves what I've got if I hold down the button. The code below also doesn't have a time out after the 1st digitalRead HIGH or true boolean (I am struggling with this).
Arduino sketch
int inPin = 2; // input pin switch
int outPin = 3; // output pin LED
long currentTime = 0; // counter
long nextTime = 0; // counter
long lastTime = 0; // counter
int patternCounter = 0; // build up
int globalValue = 0; // lock out
int breakIn = 0; // waste of time?
void setup()
{
pinMode(inPin, INPUT);
pinMode(outPin, OUTPUT);
Serial.begin(9600);
Serial.println("GO");
}
void loop(){
// boolean true, switch just for testing
if (digitalRead(inPin)==HIGH&&globalValue==0&&breakIn==0) {
Serial.println("CLEARED 1st");
delay (500); // flood protection
globalValue++;
breakIn++;
if (globalValue>0&&breakIn>0){
currentTime = millis(); // start a 'new' counter and 'listen'
if (currentTime<6000) { // less than
if (digitalRead(inPin)==HIGH) { // and true
Serial.println("CLEARED 2nd"); // cleared the stage
delay (500); // flood protection
patternCounter++;
} // if counter less
} // if true or high
if (currentTime>6000) {
Serial.println("TIMEOUT waiting 2nd"); // timed out
globalValue = 0;
patternCounter = 0;
breakIn = 0;
} // if more than
} // global master
}
// 3rd attempt
if (globalValue==1&&patternCounter==1){ // third round
nextTime = millis(); // start a 'new' counter and 'listen'
if (nextTime<6000) { // less than
if (digitalRead(inPin)==HIGH) { // and true
Serial.println("CLEARED 3rd");
delay (500); // flood protection
patternCounter++;
} // if counter less
} // if true or high
if (nextTime>6000) {
Serial.println("TIMEOUT waiting 3rd"); // timed out
globalValue = 0;
patternCounter = 0;
} // if more than
} // global master
// 4th attempt and latch
if (globalValue==1&&patternCounter==2){ // last round
lastTime = millis(); // start a 'new' counter and 'listen'
if (lastTime<6000) { // less than
if (digitalRead(inPin)==HIGH) { // and true
digitalWrite(outPin, HIGH); // LED on
Serial.println("CLEARED 4th ARRAY"); // cleared the stage
delay(500); // flood protection
} // true or high
} // counter
if (lastTime>6000) {
Serial.println("TIMEOUT waiting 4th"); // timed out
globalValue = 0;
patternCounter = 0;
} // if more than
} // global and alarm
} // loop end
That's the current sketch, I understand the counters I've used are near pointless.
Any help would be greatly appreciated!
That is a lot to wade through so I may not understand your question but the bit of code below stands out as a problem:
currentTime = millis(); // start a 'new' counter and 'listen'
if (currentTime<6000) { // less than
.....
}
Do you understand that there is no "resetting" of millis() possible and that is merely a function that returns the number of milliseconds since the program launched? It will continue to increase as long as the program is running (until it rolls over but that is a separate problem). So in the above code 'currentTime' is only going to be < 6000 very, very briefly (6 seconds) and then never again (except for the rollover condition where millis resets).
So a typical way millis() is used to track time is, in setup, to store it's current value into a variable and add your timeout period value to it:
// timeoutAmount is defined at head of program. Let's say it is 6000 (6 seconds)
nextUpdate = millis() + timeoutAmount;
Then in loop you can do the check:
if (millis() >= nextUpdate){
nextUpdate = millis() + timeoutAmount; // set up the next timeout period
// do whatever you want to do
}
Also be careful using delay() - it is easy to use for flow control but for any program with more than one thing going on it can lead to confusing and hard to solve problems.
Oh - there are more sophisticated ways of doing timing using the built-in timers on the chip to trigger interrupts but better to get the hang of things first.
I've come up with the following sketch after playing around with your help.
The sketch will almost do everything I wanted...
When it times out (T/O) after the 1st, 2nd (inCount = 1) or 3rd (inCount = 2) button press, I'd like it to revert back to the start without having to press it again and loop triggerFlash twice.
Either that or implementing another 'wait and listen' within the time out to move it to the 2nd (inCount = 1) e.t.c. but I think that may cause problems.
I know there's delay used within the flashes but that will be changed to millis(), I'm just trying to get the basic function and understanding.
const int switchPin = 2; // the number of the input pin
const int BswitchPin = 4; // the number of the input pin
const int outPin = 3;
const int thePin = 5;
long startTime; // the value returned from millis when the switch is pressed
long escapeTime; // the value returned from millis when in time out
long duration; // variable to store the duration
int inCount = 0;
int dupe = 0;
void setup()
{
pinMode(switchPin, INPUT);
pinMode(outPin, OUTPUT);
pinMode(thePin, OUTPUT);
digitalWrite(switchPin, HIGH); // turn on pull-up resistor
Serial.begin(9600);
Serial.println("Go");
digitalWrite(outPin, HIGH);
}
void loop()
{
if(inCount==0&&digitalRead(switchPin) == LOW)
{
// here if the switch is pressed
startTime = millis();
while(inCount==0&&digitalRead(switchPin) == LOW)
; // wait while the switch is still pressed
long duration = millis() - startTime;
if (duration<4000) {
Serial.println("1");
triggerFlash();
inCount++;
}
} // master 1
if (inCount>0&&inCount<4&&digitalRead(switchPin) == LOW)
{
// here if the switch is pressed
startTime = millis();
while(inCount>0&&inCount<4&&digitalRead(switchPin) == LOW)
; // wait while the switch is still pressed
long duration = millis() - startTime;
delay(500); // flood protection
if (duration>4000) { // script an escape here - formerly if (while will loop the condition)
Serial.println("T/O");
triggerFlash();
inCount = 0;
}
if (duration<4000) {
dupe = inCount + 1;
Serial.println(dupe);
inCount++;
}
}
if (inCount>=4) {
digitalWrite(thePin, HIGH);
}
} // loop
void triggerFlash() {
int i = 0;
for (i=0; i < 8; i++){
digitalWrite(outPin, LOW);
delay(100);
digitalWrite(outPin, HIGH);
delay(100);
}
}
Any ideas are very appreciated! (edited with improved counting)
The above code is actually WRONG. Please be carefull with millis() as they rollover after some time. it is only long type. So if the millis+timeout is near max(long) and millis() will rollover and start counting from zero, the millis()>=nextupdate will be false even if the timeout actually occurs.
The correct way to do this is:
unsigned long start = millis();
unsigned long timeout = MY_TIMEOUT_HERE;
...
//check if timeout occured
unisgned long now = millis();
unsigned long elapsed = now - start;
if(elapsed > timeout)
//do whatever you need to do when timeout occurs
I just implement Arduino library. hope it help your problem.
I made it to work like setTimeout and setInterval in javascript.
You can download it here, Github
This is example of my code
You can see it in action in Tinkercad
/*
Author : Meng Inventor
Contact : https://www.facebook.com/MLabpage
15 July 2022
*/
#include "simple_scheduler.h"
#define LED1_PIN 7
#define LED2_PIN 6
#define LED3_PIN 5
#define GREEN_LED_PIN 4
Task_list job_queue;
void setup()
{
Serial.begin(115200);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(LED3_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
// setInterval will run repeatly for every given time period (ms)
job_queue.setInterval(blink_green, 1000);
job_queue.setInterval(led1_on, 2000);
}
unsigned long timer = millis();
void loop()
{
job_queue.update();
}
void led1_on(){
digitalWrite(LED1_PIN, HIGH);
job_queue.setTimeout(led1_off, 250); //setTimeout will run once after given time period (ms)
}
void led1_off(){
digitalWrite(LED1_PIN, LOW);
job_queue.setTimeout(led2_on, 250);//setTimeout will run once after given time period (ms)
}
void led2_on(){
digitalWrite(LED2_PIN, HIGH);
job_queue.setTimeout(led2_off, 250);//setTimeout will run once after given time period (ms)
}
void led2_off(){
digitalWrite(LED2_PIN, LOW);
job_queue.setTimeout(led3_on, 250);//setTimeout will run once after given time period (ms)
}
void led3_on(){
digitalWrite(LED3_PIN, HIGH);
job_queue.setTimeout(led3_off, 250);//setTimeout will run once after given time period (ms)
}
void led3_off(){
digitalWrite(LED3_PIN, LOW);
}
void blink_green() {
digitalWrite(GREEN_LED_PIN,HIGH);
job_queue.setTimeout(blink_green_off, 500);
}
void blink_green_off() {
digitalWrite(GREEN_LED_PIN,LOW);
}

Resources