FileOpen is failed sometimes in MQL4 - mql4

In an EA I'm writing a couple of integer values (some OrderTickects) into a bin file.
Then I have a TP/SL modification function that loops through active trades and applies the modifications. This function is called by each tick and opens the file and if the order ticket found in the file should ignore that order without modification.
The problem is that it successfully opens the file and reads it but only for the first time.
the rest of the time it gives me an error of 0.
I have checked everything as much as I could and tracked the issue with putting lots of prints until eventually, I am pretty sure that the problem is only the failure of the FileOpen function.
THIS HAPPENED ON TESTER. I'm not sure if it will happen on the live market as well or not, because, to catch the issue on live testing would be very hard with this EA.
I should add that every time that I opened the file, I also closed it.
My only guess is that maybe when I call the FileClose function before it closes the file successfully the next call of FileOpen arrives.
Can it be the reason?
I would highly appreciate any guidance or ideas.
Do you think I can trust opening the file in the live market?
Here are the related parts of the code.
Save function is called so less often, but IsBE_Done() is called by every tick if there are one or more active trades.
void Save(int ticket)
{
int handle = FileOpen("BE1.bin", FILE_BIN | FILE_WRITE | FILE_READ);
if(handle != INVALID_HANDLE)
{
FileSeek(handle, 0, SEEK_END);
FileWriteInteger(handle, ticket);
//---
FileClose(handle);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool IsBE_Done(int ticket)
{
int handle = FileOpen("BE1.bin", FILE_BIN | FILE_WRITE | FILE_READ);
if(handle != INVALID_HANDLE)
{
FileSeek(handle, 0, SEEK_SET);
while(!FileIsEnding(handle))
{
int val = FileReadInteger(handle);
if(ticket == val)
return true;
}
//---
FileClose(handle);
}
//---
return false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void TPSL_Modification()
{
//---
if(!Modifications)
return;
//---
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
continue;
//---
if(OrderMagicNumber() != MAGICMA || OrderSymbol() != _Symbol)
continue;
//---
if(IsBE_Done(OrderTicket())) //====>>> This is what opens the file
continue;
//---

Related

Why do builds for various projects fail with ‘Operation not permitted’ using iOS on-device compiler/toolchain?

I am an intermediately skilled Linux/Unix user trying to compile software for an iPad on a (jailbroken) iPad.
Many builds (for example, make and tex-live) fail with some Operation not permitted error. This will either look like Can't exec "blah": Operation not permitted or execvp: blah: Operation not permitted where blah is aclocal, a configure script, libtool, or just about anything. Curiously, finding the offending line in a Makefile or configure script and prefixing it with sudo -u mobile -E will solve the error for that line, only for it to reappear for on a later line or in another file. Since I am running the build scripts as mobile, I do not understand how this could possibly fix the issue, yet it does. I have confirmed that making these changes does actually allow for the script to work successfully up to that point. Running the build script with sudo or sudo -u mobile -E and/or running the entire build as root does not solve the issue; with either, I still must edit build scripts to add sudo’s.
I would like to know why this is happening, and if possible how I could address the issue without editing build scripts. Any information about these types of errors would be interesting to me even if they do not solve my problem. I am aware that the permissions/security/entitlements system is unusual on iOS and would like to learn more about how it works.
I am using an iPad Pro 4 on jailbroken iOS 13.5 with the build tools from sbingner’s and MCApollo’s repos (repo.bingner.com and mcapollo.github.io/Public). In particular, I am using a build of LLVM 5 (manually installed from sbingner’s old debs), Clang 10, Darwin CC tools 927 and GNU Make 4.2.1. I have set CC, CXX, CFLAGS, etc. to point to clang-10 and my iOS 13.5 SDK with -isysroot and have confirmed that these settings are working. I would like to replace these with updated versions, but I cannot yet build these tools for myself due to this issue and a few others. I do have access to a Mac for cross-compilation if necessary, but I would rather use only my iPad because I like the challenge.
I can attach any logs necessary or provide more information if that would be useful; I do not know enough about this issue to know what information is useful. Thanks in advance for helping me!
For anyone who ends up needing to address this issue on a jailbreak that does not have a fix for this issue, I have written (pasted below) a userland hook based on the posix_spawn implementation from the source of Apple’s xnu kernel.
Compile it with Theos, and inject it into all processes spawned by your shell by setting environment variable DYLD_INSERT_LIBRARIES to the path of the resulting dylib. Note: some tweak injectors (namely libhooker, see here) reset DYLD_INSERT_LIBRARIES, so if you notice this behavior, be sure to inject only your library.
Because the implementation of the exec syscalls in iOS call out to posix_spawn, this hook fixes all of the exec-related issue’s I’ve run into so far.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <spawn.h>
// Copied from bsd/kern/kern_exec.c
#define IS_WHITESPACE(ch) ((ch == ' ') || (ch == '\t'))
#define IS_EOL(ch) ((ch == '#') || (ch == '\n'))
// Copied from bsd/sys/imgact.h
#define IMG_SHSIZE 512
// Here, we provide an alternate implementation of posix_spawn which correctly handles #!.
// This is based on the implementation of posix_spawn in bsd/kern/kern_exec.c from Apple's xnu source.
// Thus, I am fairly confident that this posix_spawn has correct behavior relative to macOS.
%hookf(int, posix_spawn, pid_t *pid, const char *orig_path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const orig_argv[], char *const envp[]) {
// Call orig before checking for anything.
// This mirrors the standard implementation of posix_spawn because it first checks if we are spawning a binary.
int err = %orig;
// %orig returns EPERM when spawning a script.
// Thus, if err is anything other than EPERM, we can just return like normal.
if (err != EPERM)
return err;
// At this point, we do not need to check for exec permissions or anything like that.
// because posix_spawn would have returned that error instead of EPERM.
// Now we open the file for reading so that we can check if it's a script.
// If it turns out not to be a script, the EPERM must be from something else
// so we just return err.
FILE *file = fopen(orig_path, "r");
if (file == NULL) {
return err;
}
if (fseek(file, 0, SEEK_SET)) {
return err;
}
// In exec_activate_image, the data buffer is filled with the first PAGE_SIZE bytes of the file.
// However, in exec_shell_imgact, only the first IMG_SHSIZE bytes are used.
// Thus, we read IMG_SHSIZE bytes out of our file.
// The buffer is filled with newlines so that if the file is not IMG_SHSIZE bytes,
// the logic reads an IS_EOL.
char vdata[IMG_SHSIZE] = {'\n'};
if (fread(vdata, 1, IMG_SHSIZE, file) < 2) { // If we couldn't read at least two bytes, it's not a script.
fclose(file);
return err;
}
// Now that we've filled the buffer, we don't need the file anymore.
fclose(file);
// Now we follow exec_shell_imgact.
// The point of this is to confirm we have a script
// and extract the usable part of the interpreter+arg string.
// Where they return -1, we don't have a shell script, so we return err.
// Where they return an error, we return that same error.
// We don't bother doing any SUID stuff because SUID scripts should be disabled anyway.
char *ihp;
char *line_startp, *line_endp;
// Make sure we have a shell script.
if (vdata[0] != '#' || vdata[1] != '!') {
return err;
}
// Try to find the first non-whitespace character
for (ihp = &vdata[2]; ihp < &vdata[IMG_SHSIZE]; ihp++) {
if (IS_EOL(*ihp)) {
// Did not find interpreter, "#!\n"
return ENOEXEC;
} else if (IS_WHITESPACE(*ihp)) {
// Whitespace, like "#! /bin/sh\n", keep going.
} else {
// Found start of interpreter
break;
}
}
if (ihp == &vdata[IMG_SHSIZE]) {
// All whitespace, like "#! "
return ENOEXEC;
}
line_startp = ihp;
// Try to find the end of the interpreter+args string
for (; ihp < &vdata[IMG_SHSIZE]; ihp++) {
if (IS_EOL(*ihp)) {
// Got it
break;
} else {
// Still part of interpreter or args
}
}
if (ihp == &vdata[IMG_SHSIZE]) {
// A long line, like "#! blah blah blah" without end
return ENOEXEC;
}
// Backtrack until we find the last non-whitespace
while (IS_EOL(*ihp) || IS_WHITESPACE(*ihp)) {
ihp--;
}
// The character after the last non-whitespace is our logical end of line
line_endp = ihp + 1;
/*
* Now we have pointers to the usable part of:
*
* "#! /usr/bin/int first second third \n"
* ^ line_startp ^ line_endp
*/
// Now, exec_shell_imgact copies the interpreter into another buffer and then null-terminates it.
// Then, it copies the entire interpreter+args into another buffer and null-terminates it for later processing into argv.
// This processing is done in exec_extract_strings, which goes through and null-terminates each argument.
// We will just do this all at once since that's much easier.
// Keep track of how many arguments we have.
int i_argc = 0;
ihp = line_startp;
while (true) {
// ihp is on the start of an argument.
i_argc++;
// Scan to the end of the argument.
for (; ihp < line_endp; ihp++) {
if (IS_WHITESPACE(*ihp)) {
// Found the end of the argument
break;
} else {
// Keep going
}
}
// Null terminate the argument
*ihp = '\0';
// Scan to the beginning of the next argument.
for (; ihp < line_endp; ihp++) {
if (!IS_WHITESPACE(*ihp)) {
// Found the next argument
break;
} else {
// Keep going
}
}
if (ihp == line_endp) {
// We've reached the end of the arg string
break;
}
// If we are here, ihp is the start of an argument.
}
// Now line_startp is a bunch of null-terminated arguments possibly padded by whitespace.
// i_argc is now the count of the interpreter arguments.
// Our new argv should look like i_argv[0], i_argv[1], i_argv[2], ..., orig_path, orig_argv[1], orig_argv[2], ..., NULL
// where i_argv is the arguments to be extracted from line_startp;
// To allocate our new argv, we need to know orig_argc.
int orig_argc = 0;
while (orig_argv[orig_argc] != NULL) {
orig_argc++;
}
// We need space for i_argc + 1 + (orig_argc - 1) + 1 char*'s
char *argv[i_argc + orig_argc + 1];
// Copy i_argv into argv
int i = 0;
ihp = line_startp;
for (; i < i_argc; i++) {
// ihp is on the start of an argument
argv[i] = ihp;
// Scan to the next null-terminator
for (; ihp < line_endp; ihp++) {
if (*ihp == '\0') {
// Found it
break;
} else {
// Keep going
}
}
// Go to the next character
ihp++;
// Then scan to the next argument.
// There must be another argument because we already counted i_argc.
for (; ihp < line_endp; ihp++) {
if (!IS_WHITESPACE(*ihp)) {
// Found it
break;
} else {
// Keep going
}
}
// ihp is on the start of an argument.
}
// Then, copy orig_path into into argv.
// We need to make a copy of orig_path to avoid issues with const.
char orig_path_copy[strlen(orig_path)+1];
strcpy(orig_path_copy, orig_path);
argv[i] = orig_path_copy;
i++;
// Now, copy orig_argv[1...] into argv.
for (int j = 1; j < orig_argc; i++, j++) {
argv[i] = orig_argv[j];
}
// Finally, add the null.
argv[i] = NULL;
// Now, our argv is setup correctly.
// Now, we can call out to posix_spawn again.
// The interpeter is in argv[0], so we use that for the path.
return %orig(pid, argv[0], file_actions, attrp, argv, envp);
}

MetaTrader Terminal 4: debugging an expert advisor on historical data

I have a MetaTrader Terminal 4 logged into a demo account.
I have written and compiled my first Expert Advisor.
I am able to debug it on a live chart, so working fine. I would like to debug it though on historical data. In the MetaEditor the Debug>Start on history data is grey out though.
Also when running the strategy tester my break points never get hit, I put the break point on the first line in the OnTick() function, so it should get hit in my understanding.
Is this because I have a demo account? If not how can I debug my script on historical data?
update code below
#property strict
//+------------------------------------------------------------------+
//| Includes and object initialization |
//+------------------------------------------------------------------+
#include <book_examples\Trade.mqh>
CTrade Trade;
CCount Count;
#include <book_examples\Timer.mqh>
CTimer Timer;
CNewBar NewBar; // keeps track of timestamp of current bar & allows us to
determine when a new bar has opened so prevents orders being opened intrabar
#include <book_examples\TrailingStop.mqh>
#include <book_examples\MoneyManagement.mqh>
#include <book_examples\Indicators.mqh>
//+------------------------------------------------------------------+
//| Input variables |
//+------------------------------------------------------------------+
// left out to keep the post short
//+------------------------------------------------------------------+
//| Enum |
//+------------------------------------------------------------------+
enum trade_action
{
NO_ACTION = 0,
LONG = 1,
SHORT = 2,
EXIT_TRADE = 3
};
//+------------------------------------------------------------------+
//| Global variable and indicators |
//+------------------------------------------------------------------+
int gBuyTicket, gSellTicket;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set magic number
Trade.SetMagicNumber(101);
Trade.SetSlippage(10);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check for bar open
bool newBar = true; // I put a breakpoint on this line
int barShift = 0;
if(TradeOnBarOpen == true)
{
newBar = NewBar.CheckNewBar(_Symbol,_Period);
barShift = 1;
}
// Trading
if(newBar == true)
{
// Money management
double lotSize = FixedLotSize;
if(UseMoneyManagement == true)
{
lotSize = MoneyManagement(_Symbol,FixedLotSize,RiskPercent,StopLoss);
}
trade_action action = calculate_signal();
// Open buy order
if(action == LONG)
{
// close sell orders
Trade.CloseAllSellOrders();
// check if we already have an open buy trade
int open_buy = Count.Buy();
if(open_buy == 0)
{
// no current existing buy position so enter
gBuyTicket = Trade.OpenBuyOrder(_Symbol,lotSize);
ModifyStopsByPoints(gBuyTicket,StopLoss,TakeProfit);
}
}
// Open sell order
else if(action == SHORT)
{
Trade.CloseAllBuyOrders();
// check if we already have an open sell trade
int open_sell = Count.Sell();
if(open_sell == 0)
{
// no current existing sell position so enter
gSellTicket = Trade.OpenSellOrder(_Symbol,lotSize);
ModifyStopsByPoints(gSellTicket,StopLoss,TakeProfit);
}
}
}
}
trade_action calculate_signal()
{
trade_action action = NO_ACTION;
// now calculate trade logic
if (some_value > some_threshold)
{
action = LONG;
}
else if (some_value < some_threshold)
{
// enter short position
action = SHORT;
}
return action;
}
Q : "Is this because I have a demo account?"
No.
Q : "If not how can I debug my script on historical data?"
In the MT4 (as-is-2020/06) you still can implement, if in a need to have such, your own "debugging"-Inspector/Monitor-agent tool to get executed during the StrategyTester-mode of the run of the MQL4 compiled-EA.
The MT4's StrategyTester is a BackTesting tool, not an (emulated)live-market Simulator-and-Debugger, it only operates the EA in synthetic(accelerated|slowed-down)-time, using a controlled-flow of synthetic-QUOTE-message(s), emulated for a given trading instrument and TimeFrame.

Writing an expert adviser in [ MQL4 ]

So if I wanted an EA in MQL4 that took the open price and when current price was 10 pips below the open it places a buy order and when it was 10 pips above the open it sold. Only one order at a time and the open changed daily.
Q1: How could that run unstopped?
Q2: Would that even be profitable?
I know this is simple for some people to write but for me it's depressing.
A1: the simplest part ...
The MQL4 Expert Advisor type-of-code's execution can be principally run unstopped, supposing the execution engine, the MetaTrader Terminal 4, is being run in a nonstop mode. While there still remain the weekends available for service & maintenance tasks, the EA code, per se can be operated infinitely long, state-fully with an automated re-entrant safe re-launch self-protection ( OnInit(){...} + OnDeinit(){...} ).
#property copyright "Copyright © 1987-2016 [MS]"
#property link "nowhere.no"
#property version "0.00"
#property strict
extern int minDist2XTO = 10; // A minimum Distance To XTO
bool aGlobalMUTEX_LOCKED = False; // LOCK "only one order at a time"
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{ // on-launch intialisation tasks go here:
return( INIT_SUCCEEDED );
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit( const int reason )
{ // pre-exit sanitisation tasks go here:
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{ // MAIN: this is being executed upon each anFxMarketEVENT's arrival
// GoLONG();
// GoSHORT();
}
//+------------------------------------------------------------------+
//| Tester function |
//+------------------------------------------------------------------+
double OnTester()
{ // back-testing utility calculation functions go here:
double retVal = 0.0;
return( retVal );
}
//+------------------------------------------------------------------+
void GoLONG()
{ // SELF-CONTROLLABLE ( might even get concurrently run in parallel to GoSHORT on dual resources )
// -------------------------------------------------------------------------------------------------------
static bool aNewBarEVENT = FALSE;
static int aLastBAR = EMPTY,
aCurrBAR = EMPTY;
static double GoLONG_LEVEL = EMPTY;
// -------------------------------------------------------------------------------------------------------
// TEST A STATE-SHIFT
RefreshRates();
aCurrBAR = iBars(_Symbol, PERIOD_D1 );
if ( aLastBAR != aCurrBAR )
{ aLastBAR = aCurrBAR; // .SET
aNewBarEVENT = TRUE; // .FLAG
// ----------------------- // .RESET in-BAR-registers
GoLONG_LEVEL = NormalizeDouble( iOpen( _Symbol, PERIOD_D1, 0 )
- minDist2XTO * _Point
);
// ----------------------
}
else
{ aNewBarEVENT = FALSE; // !FLAG
}
if ( !aGlobalMUTEX_LOCKED
&& Ask <= GoLONG_LEVEL
)
{
// XTO ... // .XTO
if ( success ) aGlobalMUTEX_LOCK = True;
}
}
//+------------------------------------------------------------------+
void GoSHORT()
{ // SELF-CONTROLLABLE ( might even get concurrently run in parallel to GoLONG on dual resources )
...
..
.
}
A2:
This question can be addressed quantitatively. Your trading idea can be tested and validated on the real-market data, so as to provide a reasonable amount of observations that either support or limit the expected performance expectations.
Without quantitative data from adequate TimeDOMAIN span of the market seasonality irregularities, the question cannot have a reasonably supported quantitative answer ( except for just an opinion ).
This goes far beyond a StackOverflow format of Q/A, but in principle back-testing ( with a support of an inbuilt mechanism of double OnTester(){...} post-processing facility ) and forward-testing ( with a support of demo-account forward runs ) are both being used in quant modelling, before any real trading strategy is ever exposed to execute any real market-risk.

pointers and linked list with shared memory

I want to get 2 programs to communicate, one (server) would store datas, and the other (client) would just access it.
I'll have to use a linked list to store datas because it won't stop storing, and then I was wondering if I could access to the whole linked list if only the first node is shared in memory.
What I mean is… are we allowed to access from the client program to the memory pointed by a shared pointer?
Sorry it seems obvious that we can not, so should I store my linked list into the shared memory, or do you think that would be awkward?
Because if I do so, I'll have to declare a shared memory for every node right?
So, to add shared memory to both programs I need the same keys, but I don't know how many keys there will be, and I can't just store it for both programs, unless I would have had already a linked list…
so I used a very very VERY awkward method that I don't even know if it works right, but I wish you can tell, which is to use ftok that is supposed to take an (url,pid) and return a key. So I assumed it would send the exact same key if I used the same url and pid, using a fake pid starting from 0 that I would increment for every element I add to the linked list… what do you think about it? Any other way to do it which would seem less… crap?
typedef struct s_shared_elem
{
char c;
struct s_shared_elem* next;
struct s_shared_elem* previous;
}shared_elem;
typedef struct s_shared_list
{
s_shared_elem* first;
s_shared_elem* last;
}shared_list;
int forthekey = 0;
char* url="/home/toor/Projet_cgi/";
shared_elem* shared_malloc(int pid, const char* url)
{
shared_elem* shm;
int shmid;
int key=ftok(url,pid);
if((shmid=shmget(key,1,IPC_CREAT | 0666)) < 0)
{
perror("shmget");
exit(1);
}
if ((shm = shmat(shmid,NULL,0)) == (shared_elem*)-1)
{
perror("shmat");
exit(1);
}
return shm;
}
void Init_shared_list(shared_list* liste)
{
liste->first = NULL;
liste->last = NULL;
}
void Add_elem(shared_list* liste)
{
shared_elem* new = shared_malloc(pid,url);
new->next = NULL;
new->previous = liste->last;
if(liste->first == NULL)
{
liste->first = new;
liste->last = new;
}
else
{
liste->last->next = new;
liste->last = new;
}
forthekey++;
}
void shared_free(shared_elem* todelete,int pid, const char* url)
{
shared_elem* shm;
int shmid;
int key=ftok(url,pid);
if((shmid=shmget(key,1,IPC_CREAT | 0666)) < 0)
{
perror("shmget");
exit(1);
}
shmdt(todelete);
shmctl(shmid,IPC_RMID,NULL);
forthekey--;
}
void Delete_list(shared_list* liste)
{
while(liste->last != liste->first)
{
shared_elem* tmp=liste->last;
liste->last=liste->last->previous;
Shared_free(tmp,pid,url);
}
Shared_free(liste->first,pid,url);
}
In share memory you can insert a whole linked list. It is useful in many cases. You do not need to create a linked list of share memory (e.g. using previous key, next key ). All you need to copy each node of linked list to the shared memory.
for example .....
process2.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main(int argc, char *argv[])
{
int shmid,i;
node *data;
if ((shmid = shmget(10, SHM_SIZE, 0644 | IPC_CREAT)) == -1) {
perror("shmget");
exit(1);
}
data = (node *)shmat(shmid, (void *)0, 0); // node is linked list
for(i=0;i<2;i++)
printf("%d\n",(data++)->item_code);
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
process1.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main(int argc, char *argv[])
{
node *SELL=NULL; // node is linked list (structure) SELL is header
insert(&SELL,"Soap",1,12.5,10);
insert(&SELL,"Pen",2,20.75,8);
display(SELL);
int shmid,i;
node *data;
if ((shmid = shmget(10, 2*sizeof(node), 0644 | IPC_CREAT)) == -1) {
perror("shmget");
exit(1);
}
data = (node *) shmat(shmid, (void *)0, 0);
for(i=0;i<2;i++)
{
*(data++)=*SELL;
SELL=SELL->next;
}
getchar();
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
Run process1.c 1st then run process2.c

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