i got this loop that works pretty well, but i cant make LAlt+key work while the loop is active.
Maybe pause it while LAlt is down and continue the loop when LAlt come back up.
Any suggestions to make LAlt work with another key at the same time while the loop is active?
loop
{
PixelGetColor, Color, 1, 1439, RGB
if Color = 0x000000
{
SendInput {Del}
Sleep 80
}
else
{
Sleep 10
}
}
return
I recommend using a Timer instead of a loop. These are much more easily interrupted. Here is an example of how you might do this with your example above:
setTimer, CheckPixelColor, 250 ; milliseconds
Return ; end auto-execute section
Thread, Interrupt, 20, 3 ; see if that works
CheckPixelColor:
if (getKeyState( "LAlt", "P")) {
Return ; do not execute below code
}
PixelGetColor, Color, 1, 1439, RGB
if Color = 0x000000
{
SendInput {Del}
Sleep 150
} else {
Sleep 10
}
return
a Timer will just do the "label" action every number of milliseconds you set when you turn it on with "setTimer".
Related
I want to make certain effects with RGB led which changes colour using PWM signals.
|¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯|
| Green || Yellow || Pink |
|__________||__________||__________|
15ms 10ms 25ms
|¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯||¯¯¯¯¯¯¯¯¯¯|
| Blue || Magneta || Blue |
|__________||__________||__________|
20ms 10ms 20ms
Based on interrupt, these led colours should be displayed on LED.
I made a task specifically for this purpose and running these effects on that task.
while(1)
{
if (indication_type == event_a)
{
led_color_green();
osDelay(15);
led_color_yellow();
osDelay(10);
led_color_pink();
osDelay(25);
}
else if (indication_type == event_b)
{
led_color_blue();
osDelay(20);
led_color_magneta();
osDelay(10);
led_color_blue();
osDelay(20);
}
else
..
..
}
Code is working but there are two issues
Each led effect is visible for 50ms and if an event change has occurred, the led keeps on executing the previous state and a new state is showed after it has exited if-else condition with delay as per current implementation. How can I change the led indication immediately after receiving an interrupt?
I think there should be some clean way to make such effects especially with led. How can I improve this code? Any code reference you can share?
I am using STM32F4 and CMSIS RTOS v2
You need to be able to make a decision about the "indication type" at every time quanta (tick), and switch the sequence immediately.
The following is an outline of one way to do that:
// Color/time descriptor for each sequence
static const struct
{
void (*setcolor)(void) // Pointer to color set function
int showtime ; // Time to display color
} indication_descriptor[][3] =
{
{ {led_color_green, 15}, {led_color_green, 15}, {led_color_green, 15} }, // event_a sequence
{ {led_color_blue, 15}, {led_color_magneta, 15}, {led_color_blue, 15} }, // event_b sequence
...
} ;
// Sequence control variables
int last_indication_type = indication_type - 1 ; // force "change" on first iteration
int current_indication = 0 ;
int current_step = 0 ;
int step_time = 0 ;
while(1)
{
// On indication change...
if( indication_type != last_indication_type )
{
// Set the indication descriptor...
if( indication_type == event_a )
{
current_indication = 0 ;
}
else if( indication_type == event_b )
{
current_indication = 1 ;
}
...
last_indication_type = indication_type ;
// Set initial indication color and time for this sequence
current_step = 0
step_time = indication_descriptor[current_indication][0].showtime ;
indication_descriptor[current_indication][0].setcolor() ;
}
// Wait one tick
osDelay(1) ;
// Decrement time in step
step_time-- ;
// If end of step...
if( step_time == 0 )
{
// Set colour and time for next step
current_step == (current_step % 3) ;
step_time = indication_descriptor[current_indication][current_step].showtime ;
indication_descriptor[current_indication][current_step].setcolor() ;
}
}
It is likely that further improvements can be made, but from the fragment you have provided it is not possible to advise. For example the data type and range of indication_type, event_a etc are not shown - it is possible that a switch-case or lookup table or even arithmetic could replace the if/else if/.../else selection block, but more information would be required.
If you need faster and more deterministic sequence interruption than 1ms you'd have to use an osTimer, with a callback function that sets a thread flag and have the task wait on a thread flag rather than osDelay(). The interrupter can then set a different thread flag to wake the task to abort the timer and switch indication. That is somewhat more complex however and likely not necessary in this case. A pattern that is useful for when hard-real-time response at the microsecond order is required. If you did that the sequence code might change thus:
...
// Set initial indication color and time for this sequence
current_step = 0
osTimerStart( step_timer, indication_descriptor[current_indication][0].showtime ) ;
indication_descriptor[current_indication][0].setcolor() ;
}
// Wait for timer expiry or event
uint32_t flags = osThreadFlagsWait( STEP_COMPLETE_FLAG | SEQUENCE_CHANGE_FLAG, osFlagsWaitAny, step_time ) ;
// If end of step...
if( (flag & STEP_COMPLETE_FLAG) != 0 )
{
// Set colour and time for next step
current_step == (current_step % 3) ;
step_time = indication_descriptor[current_indication][current_step ].showtime ;
indication_descriptor[current_indication][current_step].setcolor() ;
}
else
{
osTimerStop( step_timer ) ;
}
It has the advantage of having far fewer task context switches which may be useful if you need to reduce CPU load to achieve real-time deadlines or reduce power consumption by entering sleep for longer periods.
osWait allows you to wait for either an event or a timeout. So if you can generate a proper signal when you have an interrupt, the thread will wake-up and you can immediately switch LED state.
See https://www.keil.com/pack/doc/cmsis/RTOS/html/group__CMSIS__RTOS__Wait.html#details
Put the RTOS to work and use an RTOS timer. From the interrupt that triggers the change in indication_type, you can call your function to set the color and then start the timer for the desired color duration (this can also be done in a task context after the interrupt notifies the task). On the timeout, set the next color and restart the timer with the next color duration. When indication_type changes, you can simply set the color again and restart the timer with the new indication_type color pattern.
This is my code:
#include <wiringPi.h>
#include <stdio.h>
#define LED 18
#define IN 24
int main(void)
{
wiringPiSetupGpio ();
pinMode (LED, OUTPUT);
pinMode (IN, INPUT);
for (;;)
{
digitalWrite (LED, 1);
delay (2000);
digitalWrite (LED, 0);
delay (2000);
if (digitalRead (IN) == 1)
{
for (;;)
{
digitalWrite (LED, 1);
delay(500);
digitalWrite (LED, 0);
delay(500);
}
}
}
return 0;
}
I use a switch to go from the 2000 delay to the 500 delay. The problem is that when I press the switch it waits until the loop is over until it becomes faster. Same applies to vice versa. I need to write a function or a loop somewhere in my code that allows me to press the switch and immediately with no wait go to the slower/faster speed. I am coding this in C, on a raspberry pi using the wiring pi libraries, and gpio pin numbering.
First things first, that code as you've shown it will never deviate from a delay of 2000 simply because there's no way to exit from the initial for(;;) loop. That's probably the first thing you need to check and fix.
In terms of being able to end a delay early, you can do something like change:
delay (2000);
into:
for (i = 0; i < 2000; i += 10) {
delay (10);
if (digitalRead (IN) == 1)
break;
}
This basically exits the delay early if the switch changes, so that you'll never be waiting more than 10 time units (rather than up to 2000).
Just be aware that there may be an overhead to repeatedly calling delay. In other words, while delay(2000) may take very close to 2000 time units, two hundred calls to delay(10) may take a little more because of setup time for each call and so on.
In fact, you can probably greatly simplify your code by not using hard-coded values at all. Instead, you can have a single loop with minimal delay, with some internal controls which work out when and how to change the LED (i.e., not every time through the loop).
For example, something like this would do it. I haven't tested it since my Pi2 is still sitting in a box awaiting some time for me to play with it, but it should be pretty close:
#include <wiringPi.h>
#include <stdio.h>
#define LED 18
#define IN 24
#define RES 10
int main (void) {
int cycleTime, ledState, lastSwitch, currSwitch, timeLeft;
wiringPiSetupGpio ();
pinMode (LED, OUTPUT);
pinMode (IN, INPUT);
cycleTime = 2000; // Initial cycle time
ledState = 0; // and LED state.
lastSwitch = 0; // Previous switch state,
// Start infinite loop with first cycle time.
timeLeft = cycleTime;
for (;;) {
// Delay for minimal time and adjust time left.
delay (RES);
timeLeft = timeLeft - RES;
// Detect switch change.
currSwith = digitalRead (IN);
if (currSwitch != lastSwitch) {
// If so, store new state, change cycle time
// and force IMMEDIATE end of current cycle.
lastSwitch = currSwitch;
cycleTime = 2500 - cycleTime; // switch 500 <-> 2000
timeLeft = 0;
}
// Detect end of cycle.
if (timeLeft <= 0) {
// Toggle LED and start new cycle.
ledState = 1 - ledState;
digitalWrite (LED, ledState);
timeLeft = cycleTime;
}
}
}
Basically, you have a single loop but one which loops every ten time units rather than every 500 or 2000. You maintain a separate counter which decides when the LED should be flipped and the starting value for this counter depends on the current cycle time.
When the switch is flipped, that counter is zeroed so that it (almost) immediately changes to the next cycle.
in my next project i use 4 Leds with delay(10000). I need a function for cancel this loop and start again with a new delay value e.g. 100.
I have enabled interrupts and when i pressed a button, delay changed to 100 AFTER a round. I have to wait 10 seconds.. It is possible to restart the loop function with the new values?
Wow that was rude ignacio
At least be helpful.
You can do this but not as you have implemented.
Delay is not good to be used in this circumstance. A much better way of implementing is to use a while loop like this:
int delayLED = 10000;
int beginMillis = millis();
while( millis() - beginMillis < delayLED)
{
// insert the code for your "interrupt" here
// kinda like this
if(button pressed)
{
delayLED = 100;
break;
}
}
This is just a template not a complete answer.
Let me know if you have further questions.
Happy coding!
I have a arduino i want to blink some led light if the button is not clicked. Here is the code.
void startup(){
for (int x=0; x<=1;){
BUTTON5_state = digitalRead(START_BUTTON);
if (BUTTON5_state == HIGH ){
x++;
}
else{
blinkAll(1, 2000);
continue;
}
The problem is that its not checking the button often enough. The blink all 500 is waiting 2 seconds between each time it blinks. So you need to hold the button down for to seconds.
I want the light to blink every 2 second, but check the button "all the time". Is this possible?
Have you checked the "Blink Without Delay" tutorial in the Arduino IDE? Well, that's what you want to implement. Instead of using a delay (which is blocking) you poll the button as fast as possible and then, if millis() says that enough time has passed, you can blink.
delay is used when you want to wait; if you want to do something use other techniques.
An alternative is to use interrupts, but i suggest you to go with the first method...
2 possible solutions: create a thread, which checks the button and the original thread let the led blink. or you do the for loop faster and toggle the led only each 1000th iteration or so.
for example something like this:
for (int x=0; x<=100000;x++){
//wait to make the 1024 iteration blinking visible
BUTTON5_state = digitalRead(START_BUTTON);
if (BUTTON5_state == HIGH ){
break;
}
else if (x & (1 << 10)){ //each 1024th iteration
toggleLed();
}
}
is there a way to wait until something happens(while loop) or wait wait 10 seconds before doing a circle detection, but still displaying the video. i have tried it with while loop but if condition is not met, frames will not be displayed as code does not get the cvShow\iamge().
Yes, it's possible, but you will have to use threads. Declare a global variable bool exec_circle_detection = false; and start a 2nd thread. On this thread, call sleep(10) to wait for 10 seconds and then change exec_circle_detection to true.
In the main thread, inside the frame grabbing loop, you check if the boolean variable is set to true, and in case it isn't, you won't process the frame. This part would look something like this (in C):
char key = 0;
while (key != 27) // ESC
{
frame = cvQueryFrame(capture);
if (!frame)
{
fprintf( stderr, "!!! cvQueryFrame failed!\n" );
break;
}
if (exec_circle_detection)
{
// execute custom processing
}
// Display processed frame
cvShowImage("result", frame);
// Exit when user press ESC
key = cvWaitKey(10);
}
If you plan to do the circle detection once every 10 seconds, you will need to change exec_circle_detection to false after you execute the custom processing. On the secondary thread, adjust your code so there is a while loop changing exec_circle_detection to true every 10 seconds.
You can simply run the detection every X frames. Add a frame counter in your code, restarting to 0 when detection is perform, increase by one at each grabbed new frame, and perform detection when the counter is equal to 300 considering your video is displayed at 30 fps. You will get your 10 seconds delay between each detections.