| // Copyright 2024 The Pigweed Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
| // use this file except in compliance with the License. You may obtain a copy of | |
| // the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| // License for the specific language governing permissions and limitations under | |
| // the License. | |
| #define PW_LOG_MODULE_NAME "BLINKY" | |
| #include "blinky.h" | |
| #include <mutex> | |
| #include "modules/blinky/blinky.h" | |
| #include "pw_async2/coro.h" | |
| #include "pw_log/log.h" | |
| namespace demo { | |
| using ::pw::Allocator; | |
| using ::pw::OkStatus; | |
| using ::pw::Status; | |
| using ::pw::async2::Coro; | |
| using ::pw::async2::CoroContext; | |
| using ::pw::async2::Dispatcher; | |
| using ::pw::async2::TimeProvider; | |
| using ::pw::chrono::SystemClock; | |
| using ::pw::digital_io::DigitalInOut; | |
| using ::pw::digital_io::State; | |
| namespace { | |
| bool IsOn(DigitalInOut& io) { | |
| auto result = io.GetState(); | |
| if (!result.ok()) { | |
| return false; | |
| } | |
| return *result == State::kActive; | |
| } | |
| void TurnOn(DigitalInOut& io) { io.SetState(State::kActive).IgnoreError(); } | |
| void TurnOff(DigitalInOut& io) { io.SetState(State::kInactive).IgnoreError(); } | |
| void ToggleIo(DigitalInOut& io) { | |
| io.SetState(IsOn(io) ? State::kInactive : State::kActive).IgnoreError(); | |
| } | |
| } // namespace | |
| Coro<Status> Blinky::BlinkLoop(CoroContext, | |
| uint32_t blink_count, | |
| pw::chrono::SystemClock::duration interval) { | |
| for (uint32_t blinked = 0; blinked < blink_count || blink_count == 0; | |
| ++blinked) { | |
| { | |
| PW_LOG_INFO("LED blinking: OFF"); | |
| std::lock_guard lock(lock_); | |
| TurnOff(*monochrome_led_); | |
| } | |
| co_await time_->WaitFor(interval); | |
| { | |
| PW_LOG_INFO("LED blinking: ON"); | |
| std::lock_guard lock(lock_); | |
| TurnOn(*monochrome_led_); | |
| } | |
| co_await time_->WaitFor(interval); | |
| } | |
| { | |
| std::lock_guard lock(lock_); | |
| TurnOff(*monochrome_led_); | |
| } | |
| PW_LOG_INFO("Stopped blinking"); | |
| co_return OkStatus(); | |
| } | |
| Blinky::Blinky() | |
| : blink_task_(Coro<Status>::Empty(), [](Status) { | |
| PW_LOG_ERROR("Failed to allocate blink loop coroutine."); | |
| }) {} | |
| void Blinky::Init(Dispatcher& dispatcher, | |
| TimeProvider<SystemClock>& time, | |
| Allocator& allocator, | |
| pw::digital_io::DigitalInOut& monochrome_led) { | |
| dispatcher_ = &dispatcher; | |
| time_ = &time; | |
| allocator_ = &allocator; | |
| std::lock_guard lock(lock_); | |
| monochrome_led_ = &monochrome_led; | |
| monochrome_led_->Enable().IgnoreError(); | |
| TurnOff(*monochrome_led_); | |
| } | |
| Blinky::~Blinky() { blink_task_.Deregister(); } | |
| void Blinky::Toggle() { | |
| blink_task_.Deregister(); | |
| PW_LOG_INFO("Toggling LED"); | |
| std::lock_guard lock(lock_); | |
| ToggleIo(*monochrome_led_); | |
| } | |
| void Blinky::SetLed(bool on) { | |
| blink_task_.Deregister(); | |
| std::lock_guard lock(lock_); | |
| if (on) { | |
| PW_LOG_INFO("Setting LED on"); | |
| TurnOn(*monochrome_led_); | |
| } else { | |
| PW_LOG_INFO("Setting LED off"); | |
| TurnOff(*monochrome_led_); | |
| } | |
| } | |
| pw::Status Blinky::Blink(uint32_t blink_count, uint32_t interval_ms) { | |
| if (blink_count == 0) { | |
| PW_LOG_INFO("Blinking forever at a %ums interval", interval_ms); | |
| } else { | |
| PW_LOG_INFO( | |
| "Blinking %u times at a %ums interval", blink_count, interval_ms); | |
| } | |
| pw::chrono::SystemClock::duration interval = | |
| pw::chrono::SystemClock::for_at_least( | |
| std::chrono::milliseconds(interval_ms)); | |
| blink_task_.Deregister(); | |
| blink_task_.SetCoro(BlinkLoop(*allocator_, blink_count, interval)); | |
| dispatcher_->Post(blink_task_); | |
| return OkStatus(); | |
| } | |
| bool Blinky::IsIdle() const { | |
| std::lock_guard lock(lock_); | |
| return !blink_task_.IsRegistered(); | |
| } | |
| } // namespace demo |