NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at: www.state-machine.com/video-course and from GitHub: github.com/QuantumLeaps/modern-embedded-programming-course
I'm glad that you like the teaching approach. Please note, however, that I do NOT recommend to design your own RTOS for real use. A production-ready RTOS is more tricky than most people assume. But, designing your own RTOS kernel is an excellent LEARNING experience and every RTOS expert I know has gone through that exercise. Therefore this course uses a toy RTOS called MiROS as a teaching aid. The last lesson on RTOS will show how to switch to a professional RTOS. --MMS
That's a terrible idea. Never use an RTOS in production that you just designed and implemented! It's a convenient way to learn the internals, but that's it. There are several good enough OSes (FreeRTOS, RTX, ChibiOS, etc...) that has been in the making for 15+ years and deployed to 100000+ boards. Most of them are not just kernels but with all bells and whistles (HAL, TCP/IP stack, etc...) You can't compete with that, and you should not!
I'm not surprised that it took a while to fully digest the material. But these 2 hours is not a bad deal for gaining a true *understanding* of the stuff. --MMS
Hard to believe the gem of a course and all the material in state-machine.com is available for free. Thank you Miro, you have helped a lot in my research in RTOS.
Thanks for such a great sharing of knowledge and experience...I had never expected such nice teaching on youtube....it is difficult to found in other sources out there.
Really the best video tutorial I have seen until now about RTOS concepts, my vote of 100, thank you very much for all your work, effort and time. Best regards.
Dr.Samek, I frankly cannot thank you enough for this brilliant series. Is it all possible for you to write a blog or share a list of books regarding different areas within the field that you found really improved your understanding, to the point where it stands today. Reading books sort of gives me solid ground to stand on. This course however is at a completely different level. Thank you so much for your dedication to teach. Ovais
I already write a blog, which was originally posted on EmbeddedGurus.net, but now it has moved to the Quantum Leaps website at: www.state-machine.com/category/blog . Regarding books and other resources, you can check out: www.state-machine.com/category/books and www.state-machine.com/an . --MMS
@@StateMachineCOM Dr.Samek, I have read your blog and especially found the article on Heap and Stack very enlightening (and the embedded bare-metal build using gnu). I have listened to both the podcasts with Elicia White. I will be reading your books (already bought) once I am finished with the video lecture series, so its easier to follow. Again thank you for your reply.
Hello Miro! Thank you very much for this course. It's a great sharing of experience and knowledge and I really appreciate it. I have a simple question... In 16:25, what would be the difference between "--OS_thread[n]->timeout;" and "OS_thread[n]->timeout--;"? Best regards.
In this particular case at 16:25, the pre-decrement operator "--OS_thread[n]->timeout;" will end up doing the same to the variable as the post-decrement operator "OS_thread[n]->timeout--;" However, the pre-decrement, like "--x;" (or pre-increment, like "++x;") has a chance to be slightly more efficient. Therefore, I think it is a good habit to use the potentially more efficient alternative when possible. --MMS
Hello MIRO, thanks again for theses tutorial, I have a small question in minute 12:00 conerning the OS_onStartup(void) function: the function setting the clock SystemCoreClockUpdate(); could it remain part of the bsp_init () function because I thnk it is not linked to the interrupt config or enabling?! thanks again
Thanks Miros for these courses. I just finished going through all your lessons, with my own TIVA launchpad. I appreciate the lessons and it has helped me understand the Arm Cortex M4 core and also Operating Systems and Interrupts. One comment with this one lesson 25 and maybe it's seemed like there were errors and I had to go back and forth a lot seeing errors and eventually had to use your code on state-machine.com to verify. several bugs. This was different than your last 24 lessons which were easy to go through as I learn. . I felt this was great in terms of learning. I already knew C and have been in embedded for a while but your lessons have really helped me. But wanted to give some feedback on this one to make it better and for you to know there are people watching your videos. . Thanks again for the videos and await more. . Now that I have finished your course. I will be using the launchpad to develop using the I2C, SPI, UART and have a Bluetooth device to attach. At work I use an STM32 MCU which has a great framework, but want to use their TIVAWare drivers and am interested in your framwork to develop on the TMS4C123G MCU.
I will have to go through Lesson 25 again. I understand your intention so I will have to go through it a second time and step through it and list any confusions I have that I think someone else may experience. TECHNICALLY NO BUGS because after banging my head....LOL, I simply checked your code with mine and my bugs were my bugs, but going through it a second time may highlight where the instruction is hard to follow even when I stop the video go back and forth. But let me go through lesson 25 again, and get back with you on it. Regardless, keep going with these videos, they really are helpful.
Hi Miro, thanks for another great lesson. Are you explaining the term "blocking kernel" used in your book in any of the upcoming lessons? In any case could we call MIROS a blocking kernel after introducing the OS_delay() blocking function? ...Ok it's in RTOS part-6
Hi Miro, Quick question about your comment on why it's ok not to disable interrupts before calling OS_tick. I understand your rationale. However given that there is already a disable_interrupt statement in the systick handler, would there be any disadvantage to putting the call to the OS_tick after the disable_intterupt statement (out of an 'abundance of caution'). I guess this is a general question about potential disadvantages of putting any code in a pre-existing disabled interrupt block. The only con I can imagine may be a small probability of processor delay in handling higher priority interrupts while in the OS_tick routine.
No, it's actually not good to just throw critical sections here and there "for abundance of caution". For example, ARM Cortex-M supports prioritizing of interrupt, so SysTick can be potentially preempted by a higher-priority interrupt. In that case, a non-deterministic critical section (remember the loop inside OS_tick()?) would mess up the latency of presumably time-critical ISRs. --MMS
Hi Miro, I just wanted to say thank you for an excellent course. I have learnt so much about embedded systems programming from these courses...so thank you again. One question though, will you be preparing/publishing a companion or stand alone book at some stage to go along with these videos? I feel you should since their are not many books (at least that I know of) that will take you from theory to a working implementation. If you do I would be the first to purchase it. Best Regards, Carl.
As announced at the end of this video, the next lesson will be exactly about inter-thread communication. I will cover shared memory, semaphores, and mutexes. The example will exactly use a GPIO interrupt to make use of the on-board switches to turn the LEDs on and off. Stay tuned... --MMS
Hi, what happens to OS_Tick(); when a Thread is ready? Wht doesn't a Thread's process stretch the tick if it lasts a very long time? What am i missing?
I don't quite understand the question. OS_tick() runs in the SysTick_Handler() ISR. If the thread is "ready" (which I presume means here not waiting for a timeout), its corresponding me->timeout counter will be zero. In that case, OS_tick() will just skip that thread and do nothing to it. But there is something else in this question about "stretching the tick" that I'm not getting. Could you elaborate on what you mean? --MMS
@@StateMachineCOM I can't understand how OS_tick() keeps running when interrupt for blinky1 is finished and now it is being handled. I mean, if blinky1 now has the control and it doesn't update OS_tick() because it is not an interrupt and is just lighting leds, where does the OS_tick run in our program? Or if this doesn't make sense, how is a led being blinked while OS_tick gets updated when Blinky1.timeout happens?
@@mertkorkut9198 It seems to me that you have some incomplete understanding of interrupts. Perhaps you could watch lesson #16 "What are interrupts and how they work" ( ruclips.net/video/jP1JymlHUtc/видео.html ). Also, I would highly recommend that you actually *run* the code for this lesson and set a breakpoint in the SysTick_Handler() ISR. You'll see how often that is called and that it runs completely independently from the Blinky threads. --MMS
Hi, Additionally i have one question. Because the threads are not starting at the beggining. I debugged and found that I have to set "OS_readySet" to e.g. 1 to run first thread etc. Because when "OS_readySet" is 0 at start it never try to go to other thread. (OS_sched -> if OSready_set==0 -> curridx = 0, never trying to enter else). I have not found initialization value e.g. 16#ff for OS_readySet in Your code. So how in Your example it works without modyfing anything ?
I'm not sure what's not working for you, but the code should just run. If your version has some issues, please download the project for this lesson25 from the companion web-page at state-machine.com/video-course . Regarding the setting of the OS_readySet bitmask it *is* being set in OSThread_start(): OS_readySet |= (1U
@@StateMachineCOM Oh Yes, Thank You very much. That was the reason - I didn't added readySet during OSThread_start(). I was compared software with Your github version, but i didn't found that issue ;) I'm watching, listening, analyzing and then rewrite, so i could missed that ;) Thank a lot again ;)
You have to call the scheduler from *every* interrupt, which could make a thread ready to run. This allows the interrupt to switch context to a new task and this is what *preemptive* multitasking means. Please go back to lesson 22, where context switch was performed manually after the SysTick interrupt. --MMS
My blue and green LED blink at a slower interval(at the end of the video when the red LED is switched ON and OFF in the idle thread). I wonder where the bug could be coming from. Both seem to miss a beat with a very low intensity and short blink between the now longer intervals.
In all cases like "my project works differently than yours..." the only sane advice is to compare and see for yourself what's different. Specifically, you should: (1) download the project for this lesson from the companion web-page to this course at www.state-machine.com/video-course , (2) build and run that project on your board and verify that it behaves as in the video, and (3) compare *your* project to the official one. A good free differencing tool is WinMerge (please google for it). The tool can compare whole directories. --MMS
It seems like a hardware problem with my board. This issue does not occur when I switch a different pin in the idle thread i.e instead of switching the red LED(PB14 in my case), I switch PA12 in the idle thread instead.
Terminating threads *cleanly* is a tricky business (the keyword here is "cleanly"). Actually, you might want to google for "cleanly terminating threads" or similar. Most of the knowledgeable answers you'll find recommend that the thread terminates *itself* as opposed to being terminated externally. But even then, there are huge problems with *other* threads that might hang on the mutual exclusion mechanisms used by the killed thread or might want to somehow signal the killed thread. In any safety-related software, I would *strongly* recommend NOT to terminate threads. Instead of terminating threads, you could use an internal state machine to change state, say to "idle". --MMS
Why does the CPU/scheduler require at least one thread running? Is it due to the CPU requiring instructions to execute so it does not stall and behave unpredictably? (From chatGPT). Also, thank you for this series of videos. They have been very helpful in developing my understanding of embedded related topics.
It's not about unpredictability. The CPU simply must do something, which means executing instructions (that's all a CPU does). A stream of instructions is called a "thread"... Even if a CPU can be put into a low-power sleep mode, where it stops executing instructions, some instructions must do this. Either way, this would be the instructions inside the "idle thread." --MMS
OS_readySet is a bitmask, where each bit corresponds to the unique priority of a thread (please see video at 11:00). For example, let's say that thread of priority 5 is ready to run. The OS_readySet would then look as follows (binary) 0b0...010000. In order to check whether thread with priority 5 is ready, you have to check bit number 4, which is (1
Hi, I appreciate your videos, i have a questions regarding my code similar to you, like i want to add button with two LEDs (red and Green) by using same concepts of threads and to use flag, so that when button is pressed the red should blink. Can you please share your email or phone number so that i can send you my data for help.
NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at:
www.state-machine.com/video-course
and from GitHub:
github.com/QuantumLeaps/modern-embedded-programming-course
Complex stuff in simpler way ....brilliant.
Incomparable RTOS tutorial series in the Universe. Hats off. Turned from learning existing RTOS to designing own. Awaiting for the next video.
I'm glad that you like the teaching approach. Please note, however, that I do NOT recommend to design your own RTOS for real use. A production-ready RTOS is more tricky than most people assume. But, designing your own RTOS kernel is an excellent LEARNING experience and every RTOS expert I know has gone through that exercise. Therefore this course uses a toy RTOS called MiROS as a teaching aid. The last lesson on RTOS will show how to switch to a professional RTOS. --MMS
That's a terrible idea. Never use an RTOS in production that you just designed and implemented! It's a convenient way to learn the internals, but that's it. There are several good enough OSes (FreeRTOS, RTX, ChibiOS, etc...) that has been in the making for 15+ years and deployed to 100000+ boards. Most of them are not just kernels but with all bells and whistles (HAL, TCP/IP stack, etc...) You can't compete with that, and you should not!
It took me 2 hours to go through this 25 minutes lecture. OK, now I am ready to live forward into 1970s. Thanks!
I'm not surprised that it took a while to fully digest the material. But these 2 hours is not a bad deal for gaining a true *understanding* of the stuff. --MMS
@@StateMachineCOM Absolutely 👍 You made things so succinct, clear and detailed. Great teacher!
BEST tutorial series on youtube on embedded systems!!! Kudos Dr.!!
Dr. Miro, you are doing God's work. Thanks for such brilliant insight into RTOS.
The Best of modern and nonmodern embedded systems video course ! Thank You !
Hard to believe the gem of a course and all the material in state-machine.com is available for free. Thank you Miro, you have helped a lot in my research in RTOS.
Thanks for such a great sharing of knowledge and experience...I had never expected such nice teaching on youtube....it is difficult to found in other sources out there.
I would like to thank Dr. Miro for the great firmware content! You are a great teacher!
all of this course for free is indeed crazy i aint gonna lie
this series of embedded programming is just great.
Every.single.video.is.a.banger.
Absolutely great educational content here.
Love the video !
Really the best video tutorial I have seen until now about RTOS concepts, my vote of 100, thank you very much for all your work, effort and time. Best regards.
Dr.Samek,
I frankly cannot thank you enough for this brilliant series. Is it all possible for you to write a blog or share a list of books regarding different areas within the field that you found really improved your understanding, to the point where it stands today.
Reading books sort of gives me solid ground to stand on. This course however is at a completely different level. Thank you so much for your dedication to teach.
Ovais
I already write a blog, which was originally posted on EmbeddedGurus.net, but now it has moved to the Quantum Leaps website at: www.state-machine.com/category/blog . Regarding books and other resources, you can check out: www.state-machine.com/category/books and www.state-machine.com/an . --MMS
@@StateMachineCOM Dr.Samek, I have read your blog and especially found the article on Heap and Stack very enlightening (and the embedded bare-metal build using gnu). I have listened to both the podcasts with Elicia White.
I will be reading your books (already bought) once I am finished with the video lecture series, so its easier to follow. Again thank you for your reply.
I am indeed grateful to you.. I got a job and your lecture series has helped me a lot.
RESPECT for your knowledge, you are the GOD of embedded system.
Please make a video on how to implement malloc function in RTOS.
Hello Miro!
Thank you very much for this course. It's a great sharing of experience and knowledge and I really appreciate it.
I have a simple question... In 16:25, what would be the difference between "--OS_thread[n]->timeout;" and "OS_thread[n]->timeout--;"?
Best regards.
In this particular case at 16:25, the pre-decrement operator "--OS_thread[n]->timeout;" will end up doing the same to the variable as the post-decrement operator "OS_thread[n]->timeout--;" However, the pre-decrement, like "--x;" (or pre-increment, like "++x;") has a chance to be slightly more efficient. Therefore, I think it is a good habit to use the potentially more efficient alternative when possible. --MMS
Awesome stuff! Never seen it explained so clearly.
That Course is Magnificent
Fanstastic stuff! This is way better than my college. thank you for your efforts.
Amazing job of making a complex subject understandable.
I was desperately waiting for your next video. Thank you.
Thanks for such a great sharing of knowledge and experience
Hello MIRO, thanks again for theses tutorial, I have a small question in minute 12:00 conerning the OS_onStartup(void) function: the function setting the clock SystemCoreClockUpdate(); could it remain part of the bsp_init () function because I thnk it is not linked to the interrupt config or enabling?! thanks again
Thanks Miros for these courses. I just finished going through all your lessons, with my own TIVA launchpad. I appreciate the lessons and it has helped me understand the Arm Cortex M4 core and also Operating Systems and Interrupts. One comment with this one lesson 25 and maybe it's seemed like there were errors and I had to go back and forth a lot seeing errors and eventually had to use your code on state-machine.com to verify. several bugs. This was different than your last 24 lessons which were easy to go through as I learn.
.
I felt this was great in terms of learning. I already knew C and have been in embedded for a while but your lessons have really helped me. But wanted to give some feedback on this one to make it better and for you to know there are people watching your videos.
.
Thanks again for the videos and await more.
.
Now that I have finished your course. I will be using the launchpad to develop using the I2C, SPI, UART and have a Bluetooth device to attach. At work I use an STM32 MCU which has a great framework, but want to use their TIVAWare drivers and am interested in your framwork to develop on the TMS4C123G MCU.
Which bugs did you find? --MMS
I will have to go through Lesson 25 again. I understand your intention so I will have to go through it a second time and step through it and list any confusions I have that I think someone else may experience. TECHNICALLY NO BUGS because after banging my head....LOL, I simply checked your code with mine and my bugs were my bugs, but going through it a second time may highlight where the instruction is hard to follow even when I stop the video go back and forth. But let me go through lesson 25 again, and get back with you on it.
Regardless, keep going with these videos, they really are helpful.
amazing session.!!
Mind blowing work sir, excited for reaching till 2020
Hi Miro, thanks for another great lesson. Are you explaining the term "blocking kernel" used in your book in any of the upcoming lessons? In any case could we call MIROS a blocking kernel after introducing the OS_delay() blocking function?
...Ok it's in RTOS part-6
Hi Miro, Quick question about your comment on why it's ok not to disable interrupts before calling OS_tick. I understand your rationale. However given that there is already a disable_interrupt statement in the systick handler, would there be any disadvantage to putting the call to the OS_tick after the disable_intterupt statement (out of an 'abundance of caution'). I guess this is a general question about potential disadvantages of putting any code in a pre-existing disabled interrupt block. The only con I can imagine may be a small probability of processor delay in handling higher priority interrupts while in the OS_tick routine.
No, it's actually not good to just throw critical sections here and there "for abundance of caution". For example, ARM Cortex-M supports prioritizing of interrupt, so SysTick can be potentially preempted by a higher-priority interrupt. In that case, a non-deterministic critical section (remember the loop inside OS_tick()?) would mess up the latency of presumably time-critical ISRs. --MMS
As Always fully loaded with value
Hi Miro,
I just wanted to say thank you for an excellent course. I have learnt so much about embedded systems programming from these courses...so thank you again. One question though, will you be preparing/publishing a companion or stand alone book at some stage to go along with these videos? I feel you should since their are not many books (at least that I know of) that will take you from theory to a working implementation. If you do I would be the first to purchase it.
Best Regards,
Carl.
As announced at the end of this video, the next lesson will be exactly about inter-thread communication. I will cover shared memory, semaphores, and mutexes. The example will exactly use a GPIO interrupt to make use of the on-board switches to turn the LEDs on and off. Stay tuned... --MMS
Quantum Leaps, LLC he was asking about you writing a book
Hi,
what happens to OS_Tick(); when a Thread is ready? Wht doesn't a Thread's process stretch the tick if it lasts a very long time? What am i missing?
I don't quite understand the question. OS_tick() runs in the SysTick_Handler() ISR. If the thread is "ready" (which I presume means here not waiting for a timeout), its corresponding me->timeout counter will be zero. In that case, OS_tick() will just skip that thread and do nothing to it. But there is something else in this question about "stretching the tick" that I'm not getting. Could you elaborate on what you mean? --MMS
@@StateMachineCOM I can't understand how OS_tick() keeps running when interrupt for blinky1 is finished and now it is being handled. I mean, if blinky1 now has the control and it doesn't update OS_tick() because it is not an interrupt and is just lighting leds, where does the OS_tick run in our program? Or if this doesn't make sense, how is a led being blinked while OS_tick gets updated when Blinky1.timeout happens?
@@mertkorkut9198 It seems to me that you have some incomplete understanding of interrupts. Perhaps you could watch lesson #16 "What are interrupts and how they work" ( ruclips.net/video/jP1JymlHUtc/видео.html ). Also, I would highly recommend that you actually *run* the code for this lesson and set a breakpoint in the SysTick_Handler() ISR. You'll see how often that is called and that it runs completely independently from the Blinky threads. --MMS
Hi,
Additionally i have one question. Because the threads are not starting at the beggining. I debugged and found that I have to set "OS_readySet" to e.g. 1 to run first thread etc. Because when "OS_readySet" is 0 at start it never try to go to other thread.
(OS_sched -> if OSready_set==0 -> curridx = 0, never trying to enter else).
I have not found initialization value e.g. 16#ff for OS_readySet in Your code. So how in Your example it works without modyfing anything ?
I'm not sure what's not working for you, but the code should just run. If your version has some issues, please download the project for this lesson25 from the companion web-page at state-machine.com/video-course . Regarding the setting of the OS_readySet bitmask it *is* being set in OSThread_start(): OS_readySet |= (1U
@@StateMachineCOM Oh Yes, Thank You very much. That was the reason - I didn't added readySet during OSThread_start(). I was compared software with Your github version, but i didn't found that issue ;) I'm watching, listening, analyzing and then rewrite, so i could missed that ;)
Thank a lot again ;)
Thank You MiROS
Hello Miro,
another question! why keeping the OS_sched ( ) called from SysTick_Handler(void) ? as it is called from the OS_run() now?Thanks
You have to call the scheduler from *every* interrupt, which could make a thread ready to run. This allows the interrupt to switch context to a new task and this is what *preemptive* multitasking means. Please go back to lesson 22, where context switch was performed manually after the SysTick interrupt. --MMS
My respect and best regards for this amazing view
I was waiting for this... 😊
My blue and green LED blink at a slower interval(at the end of the video when the red LED is switched ON and OFF in the idle thread). I wonder where the bug could be coming from. Both seem to miss a beat with a very low intensity and short blink between the now longer intervals.
In all cases like "my project works differently than yours..." the only sane advice is to compare and see for yourself what's different. Specifically, you should: (1) download the project for this lesson from the companion web-page to this course at www.state-machine.com/video-course , (2) build and run that project on your board and verify that it behaves as in the video, and (3) compare *your* project to the official one. A good free differencing tool is WinMerge (please google for it). The tool can compare whole directories. --MMS
It seems like a hardware problem with my board. This issue does not occur when I switch a different pin in the idle thread i.e instead of switching the red LED(PB14 in my case), I switch PA12 in the idle thread instead.
Hi Dr. Miro - I have a request. -:) Can you also talk about the thread termination in one of your future videos?
Terminating threads *cleanly* is a tricky business (the keyword here is "cleanly"). Actually, you might want to google for "cleanly terminating threads" or similar. Most of the knowledgeable answers you'll find recommend that the thread terminates *itself* as opposed to being terminated externally. But even then, there are huge problems with *other* threads that might hang on the mutual exclusion mechanisms used by the killed thread or might want to somehow signal the killed thread. In any safety-related software, I would *strongly* recommend NOT to terminate threads. Instead of terminating threads, you could use an internal state machine to change state, say to "idle". --MMS
Why does the CPU/scheduler require at least one thread running? Is it due to the CPU requiring instructions to execute so it does not stall and behave unpredictably? (From chatGPT).
Also, thank you for this series of videos. They have been very helpful in developing my understanding of embedded related topics.
It's not about unpredictability. The CPU simply must do something, which means executing instructions (that's all a CPU does). A stream of instructions is called a "thread"... Even if a CPU can be put into a low-power sleep mode, where it stops executing instructions, some instructions must do this. Either way, this would be the instructions inside the "idle thread." --MMS
@StateMachineCOM ah I see, so the IDLE thread, in an RTOS, is a requirement of the CPU's need to execute instructions.
GOD HAS RETURNED
all the series is for the TIVA board?
Yeah, or Stellaris
I do it on STM32/GCC with minor changes in code
Great!!
It would have been interesting to measure the current used before and after the processor was put to sleep.
The right tool for the job would be to use something like the JouleScope (www.joulescope.com )
((OS_readySet & (1U
OS_readySet is a bitmask, where each bit corresponds to the unique priority of a thread (please see video at 11:00). For example, let's say that thread of priority 5 is ready to run. The OS_readySet would then look as follows (binary) 0b0...010000. In order to check whether thread with priority 5 is ready, you have to check bit number 4, which is (1
what if there is no any idle thread
If you don't wish to have an idle thread, then I don't think you can have blocking in your RTOS. --MMS
which RTOs ,this course is using?
is FREERTOS explained here?
No, this course is not based on FreeRTOS.
which RTOS is used in these videos
No one, we're developing our own RTOS
This whole series has been so helpful! But please consider removing the break music. It makes it much harder to focus on what's going on.
A word of warning! This code can corrupt the bootloader of your launchpad board. I have just destroyed two boards.
Hi, I appreciate your videos, i have a questions regarding my code similar to you, like i want to add button with two LEDs (red and Green) by using same concepts of threads and to use flag, so that when button is pressed the red should blink. Can you please share your email or phone number so that i can send you my data for help.