1.. _showcase-sense-tutorial-sim: 2 3=================== 46. Run the host app 5=================== 6Teams creating projects on top of Pigweed often create host versions of 7their apps to speed up development. "Host" means that there's no physical 8embedded device in the loop; a simulated version of the app runs directly 9on a development host computer. :ref:`target-host-device-simulator` is the 10underlying library that makes it possible to simulate apps. 11:ref:`module-pw_console` makes it easy to connect to the simulated app. Try 12out a simulated version of the ``blinky`` bringup app now: 13 14.. _REPL: https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop 15 16#. Start the simulated app: 17 18 .. tab-set:: 19 20 .. tab-item:: VS Code 21 :sync: vsc 22 23 #. In **Bazel Build Targets** expand **//apps/blinky**, then 24 right-click **:simulator_blinky (host_device_simulator_binary)**, 25 then select **Run target**. 26 27 .. admonition:: Extra macOS setup 28 29 If you see **Do you want the application "bazel" to accept incoming 30 network connections?** click **Allow**. The simulated device needs 31 to connect to local ports. 32 33 .. figure:: https://storage.googleapis.com/pigweed-media/sense/accept_incoming_network_connections.png 34 35 .. figure:: https://storage.googleapis.com/pigweed-media/sense/20240802/run_target.png 36 37 You should see output like this: 38 39 .. code-block:: console 40 41 # ... 42 INFO: Running command line: bazel-bin/apps/blinky/simulator_blinky 43 Awaiting connection on port 33000 44 45 (Your exact port may be different; that's OK.) 46 47 #. Keep this process running. This process is your simulated device. 48 It's listening on a local port for connections. In the next step 49 you connect to the simulated device over the local port. 50 51 .. tab-item:: CLI 52 :sync: cli 53 54 #. Run the following command. You should see 55 ``Awaiting connection on port XXXXX``. 56 57 .. code-block:: console 58 59 $ bazelisk run //apps/blinky:simulator_blinky 60 INFO: Analyzed target //apps/blinky:simulator_blinky (0 packages loaded, 0 targets configured). 61 INFO: Found 1 target... 62 Target //apps/blinky:simulator_blinky up-to-date: 63 bazel-bin/apps/blinky/simulator_blinky 64 INFO: Elapsed time: 0.140s, Critical Path: 0.00s 65 INFO: 1 process: 1 internal. 66 INFO: Build completed successfully, 1 total action 67 INFO: Running command line: bazel-bin/apps/blinky/simulator_blinky 68 ===================================== 69 === Pigweed Sense: Host Simulator === 70 ===================================== 71 Simulator is now running. To connect with a console, 72 either run one in a new terminal: 73 74 $ bazelisk run //<app>:simulator_console 75 76 where <app> is e.g. blinky, factory, or production, or launch 77 one from VSCode under the 'Bazel Build Targets' explorer tab. 78 79 Press Ctrl-C to exit 80 Awaiting connection on port 33000 81 82 The simulated device is now running on your development host. 83 84 #. Keep this process running. This process is your simulated device. 85 It's listening on a local port for connections. In the next step 86 you connect to the simulated device over the local port. 87 88#. Connect to the simulated app with :ref:`module-pw_console`, Pigweed's 89 extensible interactive console. 90 91 .. tab-set:: 92 93 .. tab-item:: VS Code 94 :sync: vsc 95 96 In **Bazel Build Targets** right-click the 97 **:simulator_console (native_binary)** (also under **//apps/blinky**) 98 and then select **Run target**. 99 100 .. tab-item:: CLI 101 :sync: cli 102 103 Open another terminal window or tab and run the following command. 104 105 .. code-block:: console 106 107 $ bazelisk run //apps/blinky:simulator_console 108 109 You should see ``pw_console`` start up like this: 110 111 .. figure:: https://storage.googleapis.com/pigweed-media/sense/20240802/simulator_console.png 112 113#. Look at the **Device Logs** table. You should see the simulated device 114 sending ``LED blinking`` messages every second. 115 116#. Simulate polling the Pico's temperature by typing the following into 117 **Python Repl** (bottom-left pane, look for the ``>>>`` input prompt) 118 and then pressing :kbd:`Enter`: 119 120 .. code-block:: pycon 121 122 >>> device.rpcs.board.Board.OnboardTemp() 123 124 .. admonition:: What's a REPL? 125 126 `REPL`_ stands for Read Eval Print Loop. It's an interactive 127 shell that takes your input, executes it, prints the result 128 of the execution back to your interactive shell, and then 129 repeats the loop. The console in Chrome DevTools is an example 130 of a REPL. Running ``python3`` by itself on a command line 131 opens the Python 3 REPL. 132 133 In the **Python Results** section you should see output like this: 134 135 .. code-block:: pycon 136 137 >>> device.rpcs.board.Board.OnboardTemp() 138 (Status.OK, board.OnboardTempResponse(temp=20.0)) 139 140#. Send a command over RPC that toggles the simulated device's LED: 141 142 .. code-block:: pycon 143 144 >>> device.rpcs.blinky.Blinky.ToggleLed() 145 (Status.OK, pw.protobuf.Empty()) 146 147 .. admonition:: Exercise 148 149 Can you figure out how to create a new RPC method that 150 blinks the LED twice? See 151 :ref:`showcase-sense-tutorial-appendix-rpc-solution` 152 for a solution. 153 154#. Close ``pw_console``: 155 156 .. tab-set:: 157 158 .. tab-item:: VS Code 159 :sync: vsc 160 161 Press :kbd:`Ctrl+D` twice to close ``pw_console`` and then 162 press any key to close the terminal that ``pw_console`` launched in. 163 164 .. tab-item:: CLI 165 :sync: cli 166 167 Press :kbd:`Ctrl+D` twice to close ``pw_console``. 168 169#. Stop running the simulated device: 170 171 .. tab-set:: 172 173 .. tab-item:: VS Code 174 :sync: vsc 175 176 Press :kbd:`Ctrl+C` to close the simulated device and then 177 press any key to close the terminal that it launched in. 178 179 .. tab-item:: CLI 180 :sync: cli 181 182 Press :kbd:`Ctrl+C` to close the simulated device. 183 184 .. admonition:: Troubleshooting 185 186 * **Bazel run failed: Unknown error**. You can ignore this. 187 When you close the terminal with :kbd:`Control+C` it sets 188 exit code ``255``, which the Bazel extension interprets as 189 "something went wrong". We're working on closing the simulated 190 device in a cleaner way. 191 192Of course polling a simulated temperature and toggling a simulated LED 193is rather boring but hopefully you can see how much faster your team's 194development can be when you have a simulated version of your embedded 195system to work against. 196 197Let's explore ``pw_console`` a bit more and then we'll move on to 198working with physical devices. 199 200.. _showcase-sense-tutorial-web: 201 202------------------------- 203Try the web-based console 204------------------------- 205``pw_console`` also provides a web-based UI that's high performance, 206accessible, and easy to make plugins for. Try it now: 207 208#. Launch the simulated device again: 209 210 .. tab-set:: 211 212 .. tab-item:: VS Code 213 :sync: vsc 214 215 Start up the simulated device again by going to **Bazel Build 216 Targets** right-clicking the **:simulator_blinky (native_binary)** target 217 (under **//apps/blinky**) and then selecting **Run target**. 218 219 .. caution:: 220 221 Make sure to run **:simulator_blinky**, not **:simulator_console**. 222 The first target starts the simulated device. The second target 223 attempts to connect to a simulated device. The second target naturally 224 won't work if a simulated device isn't running. 225 226 .. tab-item:: CLI 227 :sync: cli 228 229 .. code-block:: console 230 231 $ bazelisk run //apps/blinky:simulator_blinky 232 # ... 233 INFO: Running command line: bazel-bin/apps/blinky/simulator_blinky 234 Awaiting connection on port 33000 235 236 .. note:: 237 238 We had you close the simulated device in the last section and then 239 restart it again here because we're sorting out some issues around 240 simulated devices not accepting new connections reliably. 241 242#. Start the web-based console: 243 244 .. tab-set:: 245 246 .. tab-item:: VS Code 247 :sync: vsc 248 249 In **Bazel Build Targets** right-click 250 **:simulator_webconsole (native_binary)** (under **//apps/blinky**) 251 then select **Run target**. 252 253 .. tab-item:: CLI 254 :sync: cli 255 256 Open another terminal window or tab and run the following command. 257 258 .. code-block:: console 259 260 $ bazelisk run //apps/blinky:simulator_webconsole 261 262 You should see the console open in your web browser: 263 264 .. figure:: https://storage.googleapis.com/pigweed-media/sense/20240802/webconsole.png 265 266 In the logs table you should see simulated messages as before. 267 268#. Send an RPC to poll the simulated device's temperature again: 269 270 .. code-block:: pycon 271 272 >>> device.rpcs.board.Board.OnboardTemp() 273 274 .. figure:: https://storage.googleapis.com/pigweed-media/sense/20240802/webconsole_repl.png 275 276#. Type ``"00:00"`` (note the double quotes) into the search bar of either 277 of the two tables to only show logs that occurred in the first minute of 278 logging. 279 280 .. admonition:: Troubleshooting 281 282 **Don't see a search bar?** Click the magnifying glass icon. The 283 search bar is collapsed by default on narrow screens. 284 285 .. note:: 286 287 Why two tables? To demonstrate that you can filter the logs in 288 each table by different criteria. You can close a table by 289 clicking the **X** button. You can add even more tables by 290 clicking the three dot button and then selecting **Split right** 291 or **Split down**. 292 293 See :ref:`module-pw_web-log-viewer-filter` to learn more about filtering. 294 295 .. figure:: https://storage.googleapis.com/pigweed-media/sense/20240802/webconsole_filter.png 296 297#. Close the web-based console and simulated app. You're done 298 with them for now. In the terminals where you launched these, 299 each can be closed by pressing :kbd:`Control+C`. 300 301.. _showcase-sense-tutorial-sim-console-more: 302 303Learn more about pw_console 304=========================== 305Check out the :ref:`user guide <module-pw_console-user_guide>` to learn more 306about pw_console's navigation shortcuts, features, and configuration options. 307See the :ref:`embedding guide <module-pw_console-embedding>` and 308:ref:`plugin guide <module-pw_console-plugins>` to learn more about 309customizing ``pw_console`` for your project's needs. 310 311Check out :ref:`module-pw_web-log-viewer` for more information about the 312web-based version of ``pw_console``. 313 314.. _showcase-sense-tutorial-sim-summary: 315 316------- 317Summary 318------- 319Being able to run a simulated version of your product directly on your 320development host is another way that Pigweed makes embedded product 321development faster, more robust, and more reliable. For one, it's usually 322just much faster to iterate on code running on your computer versus a 323separate embedded device. For two, if you're bringing a new product to 324market, the hardware for your new device might not even exist yet! 325 326Next, head over to :ref:`showcase-sense-tutorial-sim-summary` to 327get Sense running a real Raspberry Pi Pico. 328 329-------- 330Appendix 331-------- 332 333.. _showcase-sense-tutorial-appendix-rpc-solution: 334 335Create a BlinkTwice RPC method 336============================== 337Here's one possible solution to the RPC creation exercise in 338:ref:`showcase-sense-tutorial-sim`. 339 340.. tab-set:: 341 342 .. tab-item:: blinky.proto 343 344 Declare a ``BlinkTwice()`` protobuf method and 345 ``BlinkTwiceRequest`` protobuf message. 346 347 .. code-block:: protobuf 348 349 // //modules/blinky/blinky.proto 350 351 Service Blinky { 352 // ... 353 rpc BlinkTwice(BlinkTwiceRequest) returns (pw.protobuf.Empty); 354 // ... 355 } 356 357 message BlinkIdleResponse { 358 // ... 359 } 360 361 message BlinkTwiceRequest {} 362 363 message BlinkRequest { 364 // ... 365 } 366 367 .. tab-item:: service.h 368 369 Declare the method handler in the RPC server. 370 371 .. code-block:: c++ 372 373 // //modules/blinky/service.h 374 375 // ... 376 377 pw::Status Blink(const blinky_BlinkRequest& request, pw_protobuf_Empty&); 378 379 pw::Status BlinkTwice(const blinky_BlinkTwiceRequest&, pw_protobuf_Empty&); 380 381 pw::Status Pulse(const blinky_CycleRequest& request, pw_protobuf_Empty&); 382 383 // ... 384 385 .. tab-item:: service.cc 386 387 Implement the method handler in the RPC server. 388 389 .. code-block:: c++ 390 391 // //modules/blinky/service.cc 392 393 pw::Status BlinkyService::Blink(const blinky_BlinkRequest& request, 394 pw_protobuf_Empty&) { 395 // ... 396 } 397 398 pw::Status BlinkyService::BlinkTwice(const blinky_BlinkTwiceRequest&, 399 pw_protobuf_Empty&) { 400 return blinky_.BlinkTwice(); 401 } 402 403 pw::Status BlinkyService::Pulse(const blinky_CycleRequest& request, 404 pw_protobuf_Empty&) { 405 // ... 406 } 407 408 .. tab-item:: blinky.h 409 410 Declare the ``BlinkTwice()`` hardware abstraction layer (HAL) method. 411 412 .. code-block:: c++ 413 414 // //modules/blinky/blinky.h 415 416 // ... 417 namespace am { 418 public: 419 // ... 420 pw::Status::BlinkTwice() PW_LOCKS_EXCLUDED(lock_); 421 // ... 422 } // namespace am 423 424 425 .. tab-item:: blinky.cc 426 427 Implement the ``BlinkTwice()`` HAL method. 428 429 .. code-block:: c++ 430 431 // //modules/blinky/blinky.cc 432 433 pw::Status Blinky::Blink(uint32_t blink_count, uint32_t interval_ms) { 434 // ... 435 } 436 437 pw::Status Blinky::BlinkTwice() { 438 uint32_t num_toggles = 4; 439 uint32_t interval_ms = 1000; 440 PW_LOG_INFO( 441 "Blinking %u times at a %ums interval", num_toggles / 2, interval_ms); 442 pw::chrono::SystemClock::duration interval = 443 pw::chrono::SystemClock::for_at_least( 444 std::chrono::milliseconds(interval_ms)); 445 timer_.Cancel(); 446 { 447 std::lock_guard lock(lock_); 448 monochrome_led_->TurnOff(); 449 num_toggles_ = num_toggles; 450 interval_ = interval; 451 } 452 return ScheduleToggle(); 453 } 454 455 void Blinky::Pulse(uint32_t interval_ms) { 456 // ... 457 } 458