<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://community.element14.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Forum - Recent Threads</title><link>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><lastBuildDate>Mon, 24 Aug 2026 15:49:39 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum" /><item><title>EVA Guardian[Part_5] - The Integrated System</title><link>https://community.element14.com/thread/57181?ContentTypeID=0</link><pubDate>Sat, 15 Aug 2026 17:36:00 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:8b5556fe-2506-4458-9f39-4b28794f18d1</guid><dc:creator>Sumanth_m_n</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57181?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57181/eva-guardian-part_5---the-integrated-system/rss?ContentTypeId=0</wfw:commentRss><description>&lt;div class="flex min-h-0 grow flex-col text-sm"&gt;
&lt;div class="qMYqUG_convSearchResultHighlightRoot"&gt;
&lt;div class="" data-turn-id-container="request-6a0deab6-e2f8-8321-beb6-4abed4003c0e-2" data-is-intersecting="true"&gt;
&lt;div data-conversation-screenshot-content=""&gt;
&lt;div class="flex max-w-full flex-col gap-4 grow"&gt;
&lt;div dir="auto" data-message-author-role="assistant" data-message-id="fd0e633a-d32f-469e-85d7-086c2b640994" data-message-model-slug="gpt-5-6" data-turn-start-message="true"&gt;
&lt;div class="flex w-full flex-col gap-1 empty:hidden"&gt;
&lt;div class="markdown prose dark:prose-invert wrap-break-word w-full light markdown-new-styling"&gt;
&lt;h1 id="mcetoc_1k035787u0" class="PDq2pG_selectionAnchorContainer" data-section-id="4jtnwl" data-start="481" data-end="506"&gt;When Battery Management Meets Incident Detection&lt;/h1&gt;
&lt;p data-start="559" data-end="649"&gt;In the previous posts,&lt;/p&gt;
&lt;p data-start="559" data-end="649"&gt;&lt;span&gt;Previous forum posts:&lt;/span&gt;&lt;/p&gt;
&lt;p data-start="559" data-end="649"&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57059/eva-guardian-part_1---the-idea" data-e14adj="t"&gt;Part 1 : The Idea behind EVA Guardian&lt;/a&gt;&lt;br /&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57133/eva-guardian-part_2---the-architecture" data-e14adj="t"&gt;Part 2 : The Architecture of EVA Guardian&lt;/a&gt;&lt;br /&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57152/eva-guardian-part_3---the-incident-detection-system" data-e14adj="t"&gt;Part 3 : The Incident detection system in EVA Guardian&lt;/a&gt;&lt;br /&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57164/eva-guardian-part_4---the-battery-management-system" data-e14adj="t"&gt;Part 4 : The Battery management system in EVA Guardian&lt;/a&gt;&lt;/p&gt;
&lt;p data-start="559" data-end="649"&gt;I developed the two major subsystems of EVA Guardian independently. The &lt;strong data-start="655" data-end="684"&gt;Battery Management System&lt;/strong&gt; monitors the battery and provides parameters such as voltage, current, temperature, SoC and SoH. The &lt;strong data-start="787" data-end="816"&gt;Incident Detection System&lt;/strong&gt; monitors vehicle dynamics using the MPU6500 and Edge AI. Both systems were working independently. But an EV is not made up of independent systems.&lt;/p&gt;
&lt;p data-start="967" data-end="1013"&gt;A battery condition can affect vehicle safety.&lt;/p&gt;
&lt;p data-start="1015" data-end="1057"&gt;A vehicle incident can affect the battery.&lt;/p&gt;
&lt;p data-start="1059" data-end="1151"&gt;And a communication failure between two safety-critical systems can itself become a problem.&lt;/p&gt;
&lt;p data-start="1153" data-end="1179"&gt;So the next challenge was:&lt;/p&gt;
&lt;blockquote data-start="1181" data-end="1326"&gt;
&lt;p data-start="1183" data-end="1326"&gt;&lt;strong data-start="1183" data-end="1326"&gt;How do I make these two systems communicate reliably and, more importantly, how do I know when that communication can no longer be trusted?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="1328" data-end="1380"&gt;This became the focus of this stage of EVA Guardian.&lt;/p&gt;
&lt;hr data-start="1382" data-end="1385" /&gt;
&lt;h1 id="mcetoc_1k035787u1" data-section-id="1f9d9zl" data-start="1387" data-end="1425"&gt;Creating a Master&amp;ndash;Slave Architecture&lt;/h1&gt;
&lt;p data-start="1427" data-end="1510"&gt;For the integration, I used an &lt;strong data-start="1458" data-end="1509"&gt;RS485 transceiver on both Arduino UNO Q systems&lt;/strong&gt;.The communication between the two nodes is based on UART, using:&lt;/p&gt;
&lt;ul data-start="1578" data-end="1611"&gt;
&lt;li data-section-id="1ujvz75" data-start="1578" data-end="1594"&gt;&lt;strong data-start="1580" data-end="1594"&gt;Pin 1 &amp;ndash; TX&lt;/strong&gt;&lt;/li&gt;
&lt;li data-section-id="1567tp2" data-start="1595" data-end="1611"&gt;&lt;strong data-start="1597" data-end="1611"&gt;Pin 0 &amp;ndash; RX&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="1613" data-end="1654"&gt;The architecture is intentionally simple:&lt;/p&gt;
&lt;p data-start="1656" data-end="1694"&gt;&lt;strong data-start="1656" data-end="1694"&gt;Incident Detection System &amp;rarr; Master&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="1696" data-end="1733"&gt;&lt;strong data-start="1696" data-end="1733"&gt;Battery Management System &amp;rarr; Slave&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="1735" data-end="1847"&gt;The IDS is responsible for initiating communication, while the BMS responds with its latest battery information.This gives me a deterministic communication model rather than having both systems continuously transmitting data and potentially interfering with each other.&lt;/p&gt;
&lt;hr data-start="2008" data-end="2011" /&gt;
&lt;h2 id="mcetoc_1k035787u2" data-section-id="ae0k7h" data-start="2013" data-end="2074"&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/Screenshot-2026_2D00_08_2D00_15-221836.png" /&gt;&lt;/h2&gt;
&lt;hr data-start="2076" data-end="2079" /&gt;
&lt;h1 id="mcetoc_1k035787u3" data-section-id="1p08adc" data-start="2081" data-end="2122"&gt;Designing My Own Communication Protocol&lt;/h1&gt;
&lt;p data-start="2124" data-end="2247"&gt;Rather than simply sending a raw CSV string over UART, I wanted the communication layer to have a defined packet structure. This became particularly important because the system would later need to detect communication failures and distinguish between different types of faults.&lt;/p&gt;
&lt;p data-start="2405" data-end="2469"&gt;The master sends a &lt;strong data-start="2424" data-end="2450"&gt;battery status request&lt;/strong&gt; every two seconds. The basic command packet is structured as:&lt;/p&gt;
&lt;p data-start="2515" data-end="2581"&gt;&lt;strong data-start="2515" data-end="2581"&gt;Start of Frame &amp;rarr; Payload Length &amp;rarr; Command &amp;rarr; CRC &amp;rarr; End of Frame&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="2583" data-end="2605"&gt;The start of frame is:&amp;nbsp;&lt;code data-start="2607" data-end="2618"&gt;0x55 0xAA&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="2620" data-end="2650"&gt;The battery status command is:&amp;nbsp;&lt;code data-start="2652" data-end="2658"&gt;0x01&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="2660" data-end="2691"&gt;And the packet terminates with:&lt;/p&gt;
&lt;p data-start="2693" data-end="2699"&gt;&lt;code data-start="2693" data-end="2699"&gt;0xAA&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="2701" data-end="2782"&gt;The CRC is used to provide an additional integrity check on the transmitted data.&lt;/p&gt;
&lt;hr data-start="2784" data-end="2787" /&gt;
&lt;h2 id="mcetoc_1k035787u4" data-section-id="1h2psa0" data-start="2789" data-end="2806"&gt;Master Request&lt;/h2&gt;
&lt;p data-start="2808" data-end="2835"&gt;The IDS periodically sends:&lt;/p&gt;
&lt;p data-start="2837" data-end="2844"&gt;&lt;strong data-start="2837" data-end="2844"&gt;SOF&amp;nbsp;&lt;/strong&gt;&amp;rarr; &lt;code data-start="2848" data-end="2859"&gt;0x55 0xAA&lt;/code&gt;&lt;br /&gt;&lt;strong data-start="2861" data-end="2879"&gt;Payload Length&amp;nbsp;&lt;/strong&gt;&amp;rarr; Length of command payload&lt;br /&gt;&lt;strong data-start="2910" data-end="2921"&gt;Command&amp;nbsp;&lt;/strong&gt;&amp;rarr; &lt;code data-start="2925" data-end="2931"&gt;0x01&lt;/code&gt;&lt;br /&gt;&lt;strong data-start="2933" data-end="2940"&gt;CRC&amp;nbsp;&lt;/strong&gt;&amp;rarr; Packet integrity check&lt;br /&gt;&lt;strong data-start="2968" data-end="2975"&gt;EOF&lt;/strong&gt;&amp;rarr; &lt;code data-start="2979" data-end="2985"&gt;0xAA&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="2987" data-end="3022"&gt;In simple terms, the IDS is asking:&lt;/p&gt;
&lt;blockquote data-start="3024" data-end="3072"&gt;
&lt;p data-start="3026" data-end="3072"&gt;&lt;strong data-start="3026" data-end="3072"&gt;&amp;quot;BMS, give me your latest battery status.&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-start="3074" data-end="3077" /&gt;
&lt;h1 id="mcetoc_1k035787u5" data-section-id="koq9yx" data-start="3079" data-end="3093"&gt;BMS Response&lt;/h1&gt;
&lt;p data-start="3095" data-end="3188"&gt;The BMS receives the request and prepares a response containing the latest battery telemetry.&lt;/p&gt;
&lt;p data-start="3190" data-end="3212"&gt;The response contains:&lt;/p&gt;
&lt;ul data-start="3214" data-end="3298"&gt;
&lt;li data-section-id="kq999m" data-start="3214" data-end="3223"&gt;Voltage&lt;/li&gt;
&lt;li data-section-id="jv6ha9" data-start="3224" data-end="3233"&gt;Current&lt;/li&gt;
&lt;li data-section-id="1f3vflw" data-start="3234" data-end="3247"&gt;Temperature&lt;/li&gt;
&lt;li data-section-id="kgsuzx" data-start="3248" data-end="3270"&gt;Open Circuit Voltage&lt;/li&gt;
&lt;li data-section-id="ciu4ou" data-start="3271" data-end="3286"&gt;Coulomb Count&lt;/li&gt;
&lt;li data-section-id="1o4ruf" data-start="3287" data-end="3292"&gt;SoC&lt;/li&gt;
&lt;li data-section-id="1o4ruk" data-start="3293" data-end="3298"&gt;SoH&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="3300" data-end="3354"&gt;The packet follows the same general framing structure&lt;/p&gt;
&lt;p data-start="3523" data-end="3562"&gt;The response effectively tells the IDS:&lt;/p&gt;
&lt;blockquote data-start="3564" data-end="3615"&gt;
&lt;p data-start="3566" data-end="3615"&gt;&lt;strong data-start="3566" data-end="3615"&gt;&amp;quot;Here is the latest condition of my battery.&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-start="3617" data-end="3620" /&gt;
&lt;h2 id="mcetoc_1k035787u6" data-section-id="1mddobm" data-start="3622" data-end="3698"&gt;Why Poll Every Two Seconds?&lt;/h2&gt;
&lt;p data-start="3736" data-end="3792"&gt;The IDS polls the BMS approximately every &lt;strong data-start="3778" data-end="3791"&gt;2 seconds&lt;/strong&gt;. The BMS itself is continuously updating its battery measurements, so the IDS does not need to request the data continuously at the same rate as the IMU.&lt;/p&gt;
&lt;p data-start="3948" data-end="4019"&gt;The 2-second polling interval provides a reasonable compromise between:&lt;/p&gt;
&lt;ul data-start="4021" data-end="4120"&gt;
&lt;li data-section-id="18d2mkj" data-start="4021" data-end="4048"&gt;Fresh battery information&lt;/li&gt;
&lt;li data-section-id="5brxbi" data-start="4049" data-end="4073"&gt;Communication overhead&lt;/li&gt;
&lt;li data-section-id="1y20svj" data-start="4074" data-end="4099"&gt;Processing requirements&lt;/li&gt;
&lt;li data-section-id="130bawt" data-start="4100" data-end="4120"&gt;System reliability&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="4122" data-end="4269"&gt;The IDS therefore gets a periodic snapshot of the battery condition while continuing to perform its own high-rate incident detection independently.&lt;/p&gt;
&lt;hr data-start="4271" data-end="4274" /&gt;
&lt;h1 id="mcetoc_1k035787u8" data-section-id="1t6amff" data-start="4276" data-end="4323"&gt;From Separate Telemetry to Combined Telemetry&lt;/h1&gt;
&lt;p data-start="4325" data-end="4436"&gt;Once communication was working, the next step was to bring the BMS data into the existing IDS telemetry system.&lt;/p&gt;
&lt;p data-start="4438" data-end="4494"&gt;Previously, the IDS primarily knew about things such as:&lt;/p&gt;
&lt;ul data-start="4496" data-end="4550"&gt;
&lt;li data-section-id="1ts16qe" data-start="4496" data-end="4504"&gt;Motion&lt;/li&gt;
&lt;li data-section-id="178ertb" data-start="4505" data-end="4512"&gt;Speed&lt;/li&gt;
&lt;li data-section-id="vq2a0d" data-start="4513" data-end="4524"&gt;Incidents&lt;/li&gt;
&lt;li data-section-id="be33u5" data-start="4525" data-end="4550"&gt;Accident classification&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="4552" data-end="4573"&gt;Now it also receives:&lt;/p&gt;
&lt;ul data-start="4575" data-end="4666"&gt;
&lt;li data-section-id="1y9vuyv" data-start="4575" data-end="4592"&gt;Battery voltage&lt;/li&gt;
&lt;li data-section-id="1dkyz70" data-start="4593" data-end="4610"&gt;Battery current&lt;/li&gt;
&lt;li data-section-id="wi4vs9" data-start="4611" data-end="4632"&gt;Battery temperature&lt;/li&gt;
&lt;li data-section-id="1o4ac2" data-start="4633" data-end="4638"&gt;OCV&lt;/li&gt;
&lt;li data-section-id="d3xvmm" data-start="4639" data-end="4654"&gt;Coulomb count&lt;/li&gt;
&lt;li data-section-id="1o4ruf" data-start="4655" data-end="4660"&gt;SoC&lt;/li&gt;
&lt;li data-section-id="1o4ruk" data-start="4661" data-end="4666"&gt;SoH&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="4668" data-end="4752"&gt;This information is stored in the IDS telemetry state and exposed through the WebUI. The result is a much more useful dashboard. Instead of having one screen for battery information and another for vehicle dynamics, the IDS becomes the central point from which both can be observed.&lt;/p&gt;
&lt;hr data-start="4954" data-end="4957" /&gt;
&lt;h2 id="mcetoc_1k035787u9" data-section-id="c0k3kw" data-start="4959" data-end="5027"&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/Screenshot-2026_2D00_08_2D00_15-223307.png" /&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/Screenshot-2026_2D00_08_2D00_15-223330.png" /&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/Screenshot-2026_2D00_08_2D00_15-223347.png" /&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/Screenshot-2026_2D00_08_2D00_15-223403.png" /&gt;&lt;/h2&gt;
&lt;hr data-start="5029" data-end="5032" /&gt;
&lt;h1 id="mcetoc_1k035787ua" data-section-id="115ob26" data-start="5034" data-end="5078"&gt;But What Happens When Communication Stops?&lt;/h1&gt;
&lt;p data-start="5080" data-end="5140"&gt;This was where the integration became much more interesting.&lt;/p&gt;
&lt;p data-start="5142" data-end="5168"&gt;It is easy to demonstrate:&lt;/p&gt;
&lt;blockquote data-start="5170" data-end="5225"&gt;
&lt;p data-start="5172" data-end="5225"&gt;&lt;strong data-start="5172" data-end="5225"&gt;BMS connected &amp;rarr; data received &amp;rarr; everything works.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="5227" data-end="5273"&gt;But I wanted to deliberately break the system. The communication path has three possible failure points:&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787ub" data-section-id="1uxnbef" data-start="5334" data-end="5347"&gt;Failure 1&lt;/h3&gt;
&lt;p data-start="5348" data-end="5381"&gt;&lt;strong data-start="5348" data-end="5381"&gt;IDS RS485 transceiver failure&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787uc" data-section-id="1uxnbec" data-start="5383" data-end="5396"&gt;Failure 2&lt;/h3&gt;
&lt;p data-start="5397" data-end="5430"&gt;&lt;strong data-start="5397" data-end="5430"&gt;BMS RS485 transceiver failure&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787ud" data-section-id="1uxnbed" data-start="5432" data-end="5445"&gt;Failure 3&lt;/h3&gt;
&lt;p data-start="5446" data-end="5479"&gt;&lt;strong data-start="5446" data-end="5479"&gt;Cable between the two systems&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="5481" data-end="5499"&gt;The challenge was:&lt;/p&gt;
&lt;blockquote data-start="5501" data-end="5578"&gt;
&lt;p data-start="5503" data-end="5578"&gt;&lt;strong data-start="5503" data-end="5578"&gt;If communication stops, how can the IDS determine which one has failed?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="5580" data-end="5598"&gt;Simply displaying:&lt;/p&gt;
&lt;blockquote data-start="5600" data-end="5629"&gt;
&lt;p data-start="5602" data-end="5629"&gt;&amp;quot;RS485 communication error&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="5631" data-end="5645"&gt;is not enough.&lt;/p&gt;
&lt;p data-start="5647" data-end="5716"&gt;I wanted EVA Guardian to identify the probable source of the problem.&lt;/p&gt;
&lt;hr data-start="5718" data-end="5721" /&gt;
&lt;h1 id="mcetoc_1k035787ue" data-section-id="brux4z" data-start="5723" data-end="5750"&gt;Step 1 &amp;ndash; Wait for the BMS&lt;/h1&gt;
&lt;p data-start="5752" data-end="5816"&gt;After startup, the IDS begins polling the BMS every two seconds.&lt;/p&gt;
&lt;p data-start="5818" data-end="5843"&gt;If everything is healthy:&lt;/p&gt;
&lt;p data-start="5845" data-end="5862"&gt;&lt;strong data-start="5845" data-end="5862"&gt;IDS &amp;rarr; Request&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="5864" data-end="5882"&gt;&lt;strong data-start="5864" data-end="5882"&gt;BMS &amp;rarr; Response&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="5884" data-end="5901"&gt;&lt;strong data-start="5884" data-end="5901"&gt;IDS &amp;rarr; Request&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="5903" data-end="5921"&gt;&lt;strong data-start="5903" data-end="5921"&gt;BMS &amp;rarr; Response&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="5923" data-end="5933"&gt;and so on.&lt;/p&gt;
&lt;p data-start="5935" data-end="6052"&gt;But if the IDS doesn&amp;#39;t receive a valid response for &lt;strong data-start="5987" data-end="6001"&gt;30 seconds&lt;/strong&gt;, the system assumes that something has gone wrong.&lt;/p&gt;
&lt;p data-start="6054" data-end="6133"&gt;Instead of immediately blaming the cable, the IDS begins a diagnostic sequence.&lt;/p&gt;
&lt;hr data-start="6135" data-end="6138" /&gt;
&lt;h1 id="mcetoc_1k035787uf" data-section-id="1xgd6so" data-start="6140" data-end="6181"&gt;Step 2 &amp;ndash; Test the IDS RS485 Transceiver&lt;/h1&gt;
&lt;p data-start="6183" data-end="6205"&gt;The first question is:&lt;/p&gt;
&lt;blockquote data-start="6207" data-end="6249"&gt;
&lt;p data-start="6209" data-end="6249"&gt;&lt;strong data-start="6209" data-end="6249"&gt;Is my own RS485 transceiver working?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="6251" data-end="6296"&gt;The IDS performs a local loopback diagnostic. Specific GPIO lines are controlled to configure the RS485 transceiver into its loopback/test condition.&lt;/p&gt;
&lt;p data-start="6403" data-end="6434"&gt;The diagnostic command used is:&lt;/p&gt;
&lt;p data-start="6436" data-end="6442"&gt;&lt;code data-start="6436" data-end="6442"&gt;0x02&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="6444" data-end="6457"&gt;with payload:&lt;/p&gt;
&lt;p data-start="6459" data-end="6467"&gt;&lt;code data-start="6459" data-end="6467"&gt;0x1010&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="6469" data-end="6538"&gt;The IDS sends the diagnostic pattern through its own RS485 interface.&lt;/p&gt;
&lt;p data-start="6540" data-end="6594"&gt;If the transmitted data is successfully received back:&lt;/p&gt;
&lt;blockquote data-start="6596" data-end="6628"&gt;
&lt;p data-start="6598" data-end="6628"&gt;&lt;strong data-start="6598" data-end="6628"&gt;IDS RS485 transceiver = OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="6630" data-end="6676"&gt;If the IDS cannot receive what it transmitted:&lt;/p&gt;
&lt;blockquote data-start="6678" data-end="6713"&gt;
&lt;p data-start="6680" data-end="6713"&gt;&lt;strong data-start="6680" data-end="6713"&gt;IDS RS485 transceiver = FAULT&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="6715" data-end="6775"&gt;The WebUI then reports the appropriate diagnostic condition.&lt;/p&gt;
&lt;p data-start="6777" data-end="6884"&gt;This is useful because the system doesn&amp;#39;t immediately assume that the external cable or BMS is responsible.&lt;/p&gt;
&lt;hr data-start="6961" data-end="6964" /&gt;
&lt;h1 id="mcetoc_1k035787uh" data-section-id="1djswyz" data-start="6966" data-end="7007"&gt;Step 3 &amp;ndash; Test the BMS RS485 Transceiver&lt;/h1&gt;
&lt;p data-start="7009" data-end="7062"&gt;Suppose the IDS transceiver passes its loopback test.&lt;/p&gt;
&lt;p data-start="7064" data-end="7076"&gt;We now know:&lt;/p&gt;
&lt;blockquote data-start="7078" data-end="7112"&gt;
&lt;p data-start="7080" data-end="7112"&gt;&lt;strong data-start="7080" data-end="7112"&gt;My side is probably working.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="7114" data-end="7160"&gt;But the BMS transceiver could still be faulty.&lt;/p&gt;
&lt;p data-start="7162" data-end="7314"&gt;So the next diagnostic step is to communicate with the BMS through the network connection and ask the BMS to perform the same local loopback diagnostic.&lt;/p&gt;
&lt;p data-start="7316" data-end="7341"&gt;The same command is used:&lt;/p&gt;
&lt;p data-start="7343" data-end="7362"&gt;&lt;strong data-start="7343" data-end="7355"&gt;Command:&lt;/strong&gt; &lt;code data-start="7356" data-end="7362"&gt;0x02&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="7364" data-end="7385"&gt;&lt;strong data-start="7364" data-end="7376"&gt;Payload:&lt;/strong&gt; &lt;code data-start="7377" data-end="7385"&gt;0x1010&lt;/code&gt;&lt;/p&gt;
&lt;p data-start="7387" data-end="7432"&gt;The BMS performs its own RS485 loopback test.&lt;/p&gt;
&lt;p data-start="7434" data-end="7488"&gt;If the BMS successfully receives its transmitted data:&lt;/p&gt;
&lt;blockquote data-start="7490" data-end="7522"&gt;
&lt;p data-start="7492" data-end="7522"&gt;&lt;strong data-start="7492" data-end="7522"&gt;BMS RS485 transceiver = OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="7524" data-end="7568"&gt;The BMS reports this status back to the IDS.&lt;/p&gt;
&lt;p data-start="7570" data-end="7610"&gt;If the BMS cannot complete the loopback:&lt;/p&gt;
&lt;blockquote data-start="7612" data-end="7647"&gt;
&lt;p data-start="7614" data-end="7647"&gt;&lt;strong data-start="7614" data-end="7647"&gt;BMS RS485 transceiver = FAULT&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="7649" data-end="7701"&gt;The IDS then displays this information on the WebUI.&lt;/p&gt;
&lt;hr data-start="7703" data-end="7706" /&gt;
&lt;h1 id="mcetoc_1k035787ui" data-section-id="oc7rmd" data-start="7708" data-end="7743"&gt;Step 4 &amp;ndash; So Where Is the Problem?&lt;/h1&gt;
&lt;p data-start="7745" data-end="7776"&gt;Now comes the interesting part.&lt;/p&gt;
&lt;p data-start="7778" data-end="7786"&gt;Imagine:&lt;/p&gt;
&lt;p data-start="7788" data-end="7812"&gt;&lt;strong data-start="7788" data-end="7812"&gt;IDS transceiver &amp;rarr; OK&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="7814" data-end="7838"&gt;&lt;strong data-start="7814" data-end="7838"&gt;BMS transceiver &amp;rarr; OK&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="7840" data-end="7844"&gt;but:&lt;/p&gt;
&lt;p data-start="7846" data-end="7880"&gt;&lt;strong data-start="7846" data-end="7880"&gt;IDS &amp;lt;-&amp;gt;&amp;nbsp;BMS communication &amp;rarr; FAIL&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="7882" data-end="7980"&gt;At this point, both ends have independently verified that their own RS485 hardware is functioning.&lt;/p&gt;
&lt;p data-start="7982" data-end="8018"&gt;That leaves the most likely problem:&lt;/p&gt;
&lt;blockquote data-start="8020" data-end="8086"&gt;
&lt;p data-start="8022" data-end="8086"&gt;&lt;strong data-start="8022" data-end="8086"&gt;The communication cable or physical connection between them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="8088" data-end="8119"&gt;The WebUI can therefore report:&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787uj" data-section-id="1p6xb2i" data-start="8121" data-end="8153"&gt;Communication Cable Fault&lt;/h3&gt;
&lt;p data-start="8155" data-end="8212"&gt;instead of giving the user a generic communication error.&lt;/p&gt;
&lt;p data-start="8214" data-end="8316"&gt;This is a small feature, but from a system engineering perspective, it makes a significant difference.&lt;/p&gt;
&lt;hr data-start="8318" data-end="8321" /&gt;
&lt;h2 id="mcetoc_1k035787uk" data-section-id="5i2d1e" data-start="8323" data-end="8392"&gt;[View:https://youtu.be/ciR2tdJ3-0Y:640:360]&lt;/h2&gt;
&lt;hr data-start="8394" data-end="8397" /&gt;
&lt;h1 id="mcetoc_1k035787ul" data-section-id="m2s19x" data-start="8399" data-end="8440"&gt;Why I Wanted This Diagnostic Capability&lt;/h1&gt;
&lt;p data-start="8442" data-end="8487"&gt;Imagine this system inside an actual vehicle.&lt;/p&gt;
&lt;p data-start="8489" data-end="8520"&gt;A technician receives an error:&lt;/p&gt;
&lt;blockquote data-start="8522" data-end="8553"&gt;
&lt;p data-start="8524" data-end="8553"&gt;&lt;strong data-start="8524" data-end="8553"&gt;BMS Communication Failure&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="8555" data-end="8584"&gt;What should they check first?&lt;/p&gt;
&lt;p data-start="8586" data-end="8594"&gt;The BMS? The IDS? The transceiver? The cable?&lt;/p&gt;
&lt;p data-start="8636" data-end="8698"&gt;Without diagnostics, troubleshooting becomes a manual process.&lt;/p&gt;
&lt;p data-start="8700" data-end="8801"&gt;With the diagnostic logic implemented in EVA Guardian, the system itself can narrow down the problem.&lt;/p&gt;
&lt;p data-start="8803" data-end="8814"&gt;Instead of:&lt;/p&gt;
&lt;blockquote data-start="8816" data-end="8843"&gt;
&lt;p data-start="8818" data-end="8843"&gt;&lt;strong data-start="8818" data-end="8843"&gt;Communication Failure&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="8845" data-end="8868"&gt;the system can provide:&lt;/p&gt;
&lt;blockquote data-start="8870" data-end="8891"&gt;
&lt;p data-start="8872" data-end="8891"&gt;&lt;strong data-start="8872" data-end="8891"&gt;IDS RS485 Fault&amp;nbsp;&lt;/strong&gt;or&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-start="8897" data-end="8918"&gt;
&lt;p data-start="8899" data-end="8918"&gt;&lt;strong data-start="8899" data-end="8918"&gt;BMS RS485 Fault&amp;nbsp;&lt;/strong&gt;or&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-start="8924" data-end="8955"&gt;
&lt;p data-start="8926" data-end="8955"&gt;&lt;strong data-start="8926" data-end="8955"&gt;Communication Cable Fault&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="8957" data-end="9040"&gt;This is the difference between &lt;strong data-start="8988" data-end="9011"&gt;monitoring a system&lt;/strong&gt; and &lt;strong data-start="9016" data-end="9039"&gt;diagnosing a system&lt;/strong&gt;.&lt;/p&gt;
&lt;hr data-start="9042" data-end="9045" /&gt;
&lt;h1 id="mcetoc_1k035787um" data-section-id="1xg041y" data-start="9047" data-end="9102"&gt;Another Problem: What Happens to the Power Connector?&lt;/h1&gt;
&lt;p data-start="9104" data-end="9206"&gt;After dealing with communication failures, I wanted to investigate another practical failure scenario. The physical power connection between the BMS and IDS can also deteriorate. A connector doesn&amp;#39;t necessarily go from:&lt;/p&gt;
&lt;p data-start="9327" data-end="9358"&gt;&lt;strong data-start="9327" data-end="9358"&gt;Perfect &amp;rarr; Completely Broken&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="9360" data-end="9390"&gt;It can gradually become worse. A poor connection can introduce additional resistance and therefore cause a voltage drop. So I added another voltage measurement point at the IDS side.&lt;/p&gt;
&lt;hr data-start="9546" data-end="9549" /&gt;
&lt;h1 id="mcetoc_1k035787un" data-section-id="beiol6" data-start="9551" data-end="9584"&gt;Monitoring the Power Connection&lt;/h1&gt;
&lt;p data-start="9586" data-end="9708"&gt;The IDS measures the voltage arriving through the power connection using its own voltage measurement circuit connected to:&lt;/p&gt;
&lt;p data-start="9710" data-end="9716"&gt;&lt;strong data-start="9710" data-end="9716"&gt;A0&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="9718" data-end="9796"&gt;At the same time, the BMS provides its measured battery voltage through RS485. Now I have two values:&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787uo" data-section-id="hv18j2" data-start="9822" data-end="9842"&gt;BMS-side voltage&lt;/h3&gt;
&lt;p data-start="9844" data-end="9873"&gt;Measured directly by the BMS.&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787up" data-section-id="l0p7kc" data-start="9875" data-end="9895"&gt;IDS-side voltage&lt;/h3&gt;
&lt;p data-start="9897" data-end="9933"&gt;Measured after the power connection. The system continuously compares the two.&lt;/p&gt;
&lt;p data-start="9978" data-end="9991"&gt;Conceptually:&lt;/p&gt;
&lt;p data-start="9993" data-end="10012"&gt;&lt;strong data-start="9993" data-end="10012"&gt;Battery voltage -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="10017" data-end="10036"&gt;BMS measurement -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="10041" data-end="10068"&gt;Power connector / cable -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="10073" data-end="10092"&gt;IDS measurement&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="10094" data-end="10199"&gt;The difference between these measurements gives an indication of what is happening across the connection.&lt;/p&gt;
&lt;hr data-start="10201" data-end="10204" /&gt;
&lt;h1 id="mcetoc_1k035787uq" data-section-id="1wejfwu" data-start="10206" data-end="10239"&gt;Detecting Connector Degradation&lt;/h1&gt;
&lt;p data-start="10241" data-end="10366"&gt;If the voltage difference starts becoming noticeable but the connection is still functioning, the system generates a warning. For the current prototype, I configured a threshold around &lt;strong data-start="10427" data-end="10437"&gt;500 mV&lt;/strong&gt; for the degradation warning.&lt;/p&gt;
&lt;p data-start="10468" data-end="10508"&gt;The WebUI can display a message such as:&lt;/p&gt;
&lt;blockquote data-start="10510" data-end="10614"&gt;
&lt;p data-start="10512" data-end="10614"&gt;️&amp;nbsp;&lt;strong data-start="10515" data-end="10614"&gt;There is a connector degradation. Please show it to the mechanic soon before any severe damage.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="10616" data-end="10680"&gt;The purpose is not to wait until the connector completely fails. The objective is to identify a potentially deteriorating connection early.&lt;/p&gt;
&lt;hr data-start="10758" data-end="10761" /&gt;
&lt;h1 id="mcetoc_1k035787ur" data-section-id="8eeu21" data-start="10763" data-end="10794"&gt;Detecting a Broken Connection&lt;/h1&gt;
&lt;p data-start="10796" data-end="10928"&gt;The other extreme is a significant voltage drop indicating that the power path may have become disconnected or severely compromised. In this situation, the system moves from a warning condition to an error condition.&lt;/p&gt;
&lt;p data-start="11015" data-end="11043"&gt;The WebUI can then indicate:&lt;/p&gt;
&lt;blockquote data-start="11045" data-end="11076"&gt;
&lt;p data-start="11047" data-end="11076"&gt;&lt;strong data-start="11050" data-end="11076"&gt;Power connection fault&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="11078" data-end="11126"&gt;This gives the user a clear distinction between:&lt;/p&gt;
&lt;p data-start="11128" data-end="11139"&gt;&lt;strong data-start="11128" data-end="11139"&gt;Warning&amp;nbsp;&lt;/strong&gt;and&amp;nbsp;&lt;strong data-start="11146" data-end="11166"&gt;Critical failure&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="11168" data-end="11171" /&gt;
&lt;h2 id="mcetoc_1k035787us" data-section-id="he32y0" data-start="11173" data-end="11246"&gt;[View:https://youtu.be/ZFzXiJqRlYU:640:360]&lt;/h2&gt;
&lt;p data-start="11364" data-end="11500"&gt;In this demonstration, I intentionally introduce a degraded connection and observe the voltage difference between the BMS and IDS sides.&lt;/p&gt;
&lt;hr data-start="11502" data-end="11505" /&gt;
&lt;h1 id="mcetoc_1k035787uu" data-section-id="56yspc" data-start="11507" data-end="11548"&gt;Making Accident Detection More Reliable&lt;/h1&gt;
&lt;p data-start="11550" data-end="11641"&gt;The integration also gave me an opportunity to improve the accident notification mechanism. One of the problems with a pure ML-based event trigger is that a single inference window can occasionally produce an incorrect classification.&lt;/p&gt;
&lt;p data-start="11787" data-end="11813"&gt;Imagine the model outputs:&lt;/p&gt;
&lt;p data-start="11815" data-end="11833"&gt;&lt;strong data-start="11815" data-end="11833"&gt;Accident &amp;ndash; 91%&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="11835" data-end="11860"&gt;for one inference window. Should that immediately trigger an emergency message? I decided that for this system, the answer should be:&lt;/p&gt;
&lt;blockquote data-start="11972" data-end="11986"&gt;
&lt;p data-start="11974" data-end="11986"&gt;&lt;strong data-start="11974" data-end="11986"&gt;Not yet.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="11988" data-end="12163"&gt;Instead, the system now requires the model to classify the event as &lt;strong data-start="12056" data-end="12121"&gt;Accident across more than three consecutive inference windows&lt;/strong&gt; before triggering the emergency response.&lt;/p&gt;
&lt;p data-start="12165" data-end="12178"&gt;Conceptually:&lt;/p&gt;
&lt;p data-start="12180" data-end="12192"&gt;&lt;strong data-start="12180" data-end="12192"&gt;Accident -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="12197" data-end="12209"&gt;Accident -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="12214" data-end="12226"&gt;Accident -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="12231" data-end="12253"&gt;Accident confirmed -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="12258" data-end="12276"&gt;Telegram alert&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="12278" data-end="12342"&gt;This gives the system an additional layer of event confirmation.&lt;/p&gt;
&lt;hr data-start="12344" data-end="12347" /&gt;
&lt;h1 id="mcetoc_1k035787uv" data-section-id="gl1qi1" data-start="12349" data-end="12385"&gt;Preventing Telegram Alert Flooding&lt;/h1&gt;
&lt;p data-start="12387" data-end="12435"&gt;Another practical issue appeared during testing.&lt;/p&gt;
&lt;p data-start="12437" data-end="12512"&gt;If the accident condition remains active, the model can continue producing:&lt;/p&gt;
&lt;blockquote data-start="12514" data-end="12566"&gt;
&lt;p data-start="12516" data-end="12566"&gt;Accident&lt;br data-start="12524" data-end="12527" /&gt; Accident&lt;br data-start="12537" data-end="12540" /&gt; Accident&lt;br data-start="12550" data-end="12553" /&gt; Accident...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="12568" data-end="12673"&gt;Without protection, the Telegram system could potentially send multiple notifications for the same event.&lt;/p&gt;
&lt;p data-start="12675" data-end="12741"&gt;To prevent this, I implemented an &lt;strong data-start="12709" data-end="12740"&gt;accident cooldown mechanism&lt;/strong&gt;. Once an emergency alert has been dispatched, the system temporarily suppresses additional alerts for the same event.&lt;/p&gt;
&lt;p data-start="12861" data-end="12893"&gt;This creates a cleaner workflow:&lt;/p&gt;
&lt;p data-start="12895" data-end="12908"&gt;&lt;strong data-start="12895" data-end="12908"&gt;Detection&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="12910" data-end="12928"&gt;&amp;rarr; &lt;strong data-start="12912" data-end="12928"&gt;Confirmation&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="12930" data-end="12962"&gt;&amp;rarr; &lt;strong data-start="12932" data-end="12962"&gt;One emergency notification&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="12964" data-end="12978"&gt;&amp;rarr; &lt;strong data-start="12966" data-end="12978"&gt;Cooldown&lt;/strong&gt;&lt;/p&gt;
&lt;p data-start="12980" data-end="13046"&gt;rather than flooding the emergency contact with repeated messages.&lt;/p&gt;
&lt;hr data-start="13120" data-end="13123" /&gt;
&lt;h2 id="mcetoc_1k035787u10" data-section-id="p70fam" data-start="13053" data-end="13118"&gt;The Complete Data Flow&lt;/h2&gt;
&lt;p data-start="13151" data-end="13248"&gt;At this point, the integrated system has several independent data paths operating simultaneously.&lt;/p&gt;
&lt;h3 id="mcetoc_1k035787u12" data-section-id="1wg8ezc" data-start="13250" data-end="13270"&gt;Vehicle Dynamics&lt;/h3&gt;
&lt;p data-start="13272" data-end="13283"&gt;&lt;strong data-start="13272" data-end="13283"&gt;MPU6500 -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13288" data-end="13309"&gt;42 Hz acquisition -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13314" data-end="13331"&gt;EMA filtering -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13336" data-end="13357"&gt;Edge AI inference -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13362" data-end="13389"&gt;Incident classification&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="13391" data-end="13394" /&gt;
&lt;h3 id="mcetoc_1k035787u13" data-section-id="6ybnqj" data-start="13396" data-end="13407"&gt;Battery&lt;/h3&gt;
&lt;p data-start="13409" data-end="13444"&gt;&lt;strong data-start="13409" data-end="13444"&gt;Voltage / Current / Temperature -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13449" data-end="13467"&gt;BMS processing -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13472" data-end="13507"&gt;SoC / SoH / OCV / Coulomb Count -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13512" data-end="13521"&gt;RS485 -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13526" data-end="13533"&gt;IDS&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="13535" data-end="13538" /&gt;
&lt;h3 id="mcetoc_1k035787u14" data-section-id="c3g0cy" data-start="13540" data-end="13555"&gt;Diagnostics&lt;/h3&gt;
&lt;p data-start="13557" data-end="13574"&gt;&lt;strong data-start="13557" data-end="13574"&gt;RS485 timeout -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13579" data-end="13600"&gt;IDS loopback test -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13605" data-end="13626"&gt;BMS loopback test -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13631" data-end="13661"&gt;Cable fault identification&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="13663" data-end="13666" /&gt;
&lt;h3 id="mcetoc_1k035787u15" data-section-id="5b43oa" data-start="13668" data-end="13687"&gt;Power Connector&lt;/h3&gt;
&lt;p data-start="13689" data-end="13704"&gt;&lt;strong data-start="13689" data-end="13704"&gt;BMS voltage -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13709" data-end="13729"&gt;IDS-side voltage -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13734" data-end="13756"&gt;Voltage difference -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13761" data-end="13790"&gt;Connector warning / fault&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="13792" data-end="13795" /&gt;
&lt;h3 id="mcetoc_1k035787u16" data-section-id="7ffwto" data-start="13797" data-end="13819"&gt;Emergency Response&lt;/h3&gt;
&lt;p data-start="13821" data-end="13843"&gt;&lt;strong data-start="13821" data-end="13843"&gt;Confirmed accident -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13848" data-end="13860"&gt;Telegram -&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;strong data-start="13865" data-end="13880"&gt;WebUI alert&lt;/strong&gt;&lt;/p&gt;
&lt;hr data-start="13882" data-end="13885" /&gt;
&lt;p data-start="13887" data-end="13947"&gt;All these paths finally converge at the IDS telemetry layer.&lt;/p&gt;
&lt;hr data-start="13949" data-end="13952" /&gt;
&lt;h1 id="mcetoc_1k035787u17" data-section-id="ydnela" data-start="13954" data-end="13981"&gt;And Now... Let&amp;#39;s Break It&lt;/h1&gt;
&lt;p data-start="13983" data-end="14048"&gt;For me, this is one of the most interesting parts of the project.&lt;/p&gt;
&lt;p data-start="14050" data-end="14179"&gt;Instead of only showing the system working perfectly, I wanted to demonstrate what happens when I intentionally introduce faults.1.&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li id="mcetoc_1k035787u18" data-section-id="qo9cje" data-start="14181" data-end="14208"&gt;&lt;strong&gt;Pull the RS485 cable :&amp;nbsp;&lt;/strong&gt;Does the system identify the communication failure.&lt;/li&gt;
&lt;li data-section-id="qo9cje" data-start="14181" data-end="14208"&gt;&lt;strong&gt;Simulate a transceiver problem :&amp;nbsp;&lt;/strong&gt;Can it distinguish an IDS-side fault from a BMS-side fault?&lt;/li&gt;
&lt;li data-section-id="qo9cje" data-start="14181" data-end="14208"&gt;&lt;strong&gt;Introduce connector degradation:&lt;/strong&gt; Does the voltage comparison detect it&lt;/li&gt;
&lt;li data-section-id="qo9cje" data-start="14181" data-end="14208"&gt;&lt;strong&gt;Simulate an accident: &lt;/strong&gt;Does the ML model confirm the event before sending the alert&lt;/li&gt;
&lt;li data-section-id="qo9cje" data-start="14181" data-end="14208"&gt;&lt;strong&gt;Trigger a confirmed accident:&lt;/strong&gt; Does the Telegram notification reach the registered user?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-start="14630" data-end="14716"&gt;These tests make the project much more representative of an actual engineering system.&lt;/p&gt;
&lt;hr data-start="14718" data-end="14721" /&gt;
&lt;h1 id="mcetoc_1k035787u1d" data-section-id="1r7wpr5" data-start="14723" data-end="14768"&gt;Complete Integrated System Demonstration&lt;br /&gt;[View:https://youtu.be/ZM7amrmEOIg:640:360]&lt;/h1&gt;
&lt;p&gt;This video demonstrates the complete system operating as a single platform, with the BMS and IDS communicating through RS485 while the WebUI displays the combined telemetry.&lt;/p&gt;
&lt;hr data-start="15023" data-end="15026" /&gt;
&lt;h1 id="mcetoc_1k035787u1e" data-section-id="1cj8fcf" data-start="15028" data-end="15058"&gt;Automatic Power Switching&lt;/h1&gt;
&lt;p&gt;[View:https://youtu.be/iJWrgn_RvO4:640:360]&lt;br /&gt;This demonstration focuses specifically on the safety relay and automatic power switching mechanism.&lt;/p&gt;
&lt;hr data-start="15224" data-end="15227" /&gt;
&lt;h1 id="mcetoc_1k035787u1f" data-section-id="11e2l22" data-start="15229" data-end="15263"&gt;Communication Fault Detection&lt;/h1&gt;
&lt;p data-start="15265" data-end="15326"&gt;&lt;strong data-start="15265" data-end="15326"&gt;[View:https://youtu.be/ciR2tdJ3-0Y:640:360]&lt;br /&gt;&lt;/strong&gt;Here I intentionally interrupt the communication link and demonstrate how EVA Guardian performs its diagnostic sequence.&lt;/p&gt;
&lt;hr data-start="15450" data-end="15453" /&gt;
&lt;h1 id="mcetoc_1k035787u1g" data-section-id="4x6ulr" data-start="15455" data-end="15487"&gt;Power Connector Degradation&lt;/h1&gt;
&lt;p data-start="15489" data-end="15552"&gt;&lt;strong data-start="15489" data-end="15552"&gt;[View:https://youtu.be/ZFzXiJqRlYU:640:360]&lt;br /&gt;&lt;/strong&gt;This demonstration shows how the system compares the BMS-side and IDS-side voltage to identify a potentially degrading power connection.&lt;/p&gt;
&lt;hr data-start="15692" data-end="15695" /&gt;
&lt;h1 id="mcetoc_1k035787u1h" data-section-id="m67aon" data-start="15697" data-end="15735"&gt;Moving From Electronics to a Vehicle&lt;/h1&gt;
&lt;p data-start="15737" data-end="15793"&gt;The electronics are now beginning to look like a system. But I wanted the final demonstration to look like a vehicle rather than a collection of development boards sitting on a table. So I started building a small &lt;strong data-start="15953" data-end="15975"&gt;cardboard EV model&lt;/strong&gt;. The intention is to physically represent where the different subsystems would exist in an actual vehicle and route the wiring accordingly. The model is still under development, but it will provide the physical foundation for the final demonstration.&lt;/p&gt;
&lt;hr data-start="16288" data-end="16291" /&gt;
&lt;h1 id="mcetoc_1k035787u1j" data-section-id="1mdehow" data-start="16293" data-end="16317"&gt;The Finishing Touch&lt;/h1&gt;
&lt;p data-start="16319" data-end="16390"&gt;&lt;strong data-start="16319" data-end="16390"&gt;[View:https://youtu.be/CVnq63eEgyo:640:360]&lt;br /&gt;&lt;/strong&gt;&lt;em data-start="16392" data-end="16491"&gt;This part of the physical model is currently in progress and will be completed in the next stage.&lt;/em&gt;&lt;/p&gt;
&lt;hr data-start="16493" data-end="16496" /&gt;
&lt;h1 id="mcetoc_1k035787u1k" data-section-id="742nu3" data-start="16498" data-end="16517"&gt;What Has Changed?&lt;/h1&gt;
&lt;p data-start="16519" data-end="16592"&gt;Looking back at the beginning of the project, I had two separate systems.&lt;/p&gt;
&lt;p data-start="16594" data-end="16615"&gt;The BMS could answer:&lt;/p&gt;
&lt;blockquote data-start="16617" data-end="16649"&gt;
&lt;p data-start="16619" data-end="16649"&gt;&lt;strong data-start="16619" data-end="16649"&gt;&amp;quot;How is my battery doing?&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="16651" data-end="16672"&gt;The IDS could answer:&lt;/p&gt;
&lt;blockquote data-start="16674" data-end="16714"&gt;
&lt;p data-start="16676" data-end="16714"&gt;&lt;strong data-start="16676" data-end="16714"&gt;&amp;quot;What is happening to my vehicle?&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="16716" data-end="16771"&gt;After this integration, the system can start answering:&lt;/p&gt;
&lt;blockquote data-start="16773" data-end="16878"&gt;
&lt;p data-start="16775" data-end="16878"&gt;&lt;strong data-start="16775" data-end="16878"&gt;&amp;quot;How is my vehicle doing, how is its battery doing, and can I trust the information I&amp;#39;m receiving?&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="16880" data-end="16925"&gt;That last question is particularly important. Because a smart system should not only provide information. It should also know when &lt;strong data-start="17013" data-end="17057"&gt;the information itself may be unreliable&lt;/strong&gt;. That is why the communication diagnostics, connector monitoring and fault handling became such an important part of this stage.&lt;/p&gt;
&lt;hr data-start="17189" data-end="17192" /&gt;
&lt;h1 id="mcetoc_1k035787u1l" data-section-id="1h02p40" data-start="17194" data-end="17208"&gt;What&amp;#39;s Next?&lt;/h1&gt;
&lt;p data-start="17210" data-end="17301"&gt;The next stage is to move the integrated electronics into the &lt;strong data-start="17272" data-end="17300"&gt;physical EV demonstrator&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-start="17303" data-end="17434"&gt;I am currently working on the cardboard vehicle model that will house the BMS, IDS, power switching and communication architecture.&lt;/p&gt;
&lt;p data-start="17436" data-end="17465"&gt;The next phase will focus on:&lt;/p&gt;
&lt;ul data-start="17467" data-end="17772"&gt;
&lt;li data-section-id="1myzxtw" data-start="17467" data-end="17519"&gt;Integrating the electronics into the vehicle model&lt;/li&gt;
&lt;li data-section-id="1567ruq" data-start="17520" data-end="17569"&gt;Finalizing the automatic safety power switching&lt;/li&gt;
&lt;li data-section-id="1cutsmj" data-start="17570" data-end="17590"&gt;Refining the WebUI&lt;/li&gt;
&lt;li data-section-id="mvg60j" data-start="17591" data-end="17637"&gt;Improving fault indication and system status&lt;/li&gt;
&lt;li data-section-id="wb2j41" data-start="17638" data-end="17693"&gt;Demonstrating the complete end-to-end safety workflow&lt;/li&gt;
&lt;li data-section-id="1uiwuk1" data-start="17694" data-end="17772"&gt;Presenting the system as a single EV platform rather than individual modules&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="17774" data-end="17821"&gt;The goal for the final demonstration is simple:&lt;/p&gt;
&lt;blockquote data-start="17823" data-end="17869"&gt;
&lt;p data-start="17825" data-end="17869"&gt;&lt;strong data-start="17825" data-end="17869"&gt;Don&amp;#39;t just show that EVA Guardian works.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-start="17871" data-end="17921"&gt;
&lt;p data-start="17873" data-end="17921"&gt;&lt;strong data-start="17873" data-end="17921"&gt;Show what happens when something goes wrong.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="17923" data-end="17982"&gt;That is where I believe the real value of the project lies.&lt;/p&gt;
&lt;hr data-start="17984" data-end="17987" /&gt;
&lt;h1 id="mcetoc_1k035787v1m" data-section-id="1329ug4" data-start="17989" data-end="18005"&gt;Final Thoughts&lt;/h1&gt;
&lt;p data-start="18007" data-end="18182"&gt;This stage has been one of the most interesting parts of EVA Guardian because I moved away from developing individual features and started thinking about &lt;strong data-start="18161" data-end="18181"&gt;system behaviour&lt;/strong&gt;. A real vehicle doesn&amp;#39;t operate in perfect conditions.&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="18007" data-end="18182"&gt;Cables can fail.&lt;/li&gt;
&lt;li data-start="18007" data-end="18182"&gt;Connectors can degrade.&lt;/li&gt;
&lt;li data-start="18007" data-end="18182"&gt;Sensors can produce abnormal values.&lt;/li&gt;
&lt;li data-start="18007" data-end="18182"&gt;Communication links can disappear.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-start="18356" data-end="18395"&gt;And an accident can happen at any time. The objective of EVA Guardian is therefore not simply to detect these events. It is to &lt;strong data-start="18485" data-end="18532"&gt;identify, communicate, diagnose and respond&lt;/strong&gt; to them. And with the BMS and IDS now working together, EVA Guardian is getting much closer to the original vision:&lt;/p&gt;
&lt;blockquote data-start="18651" data-end="18793"&gt;
&lt;p data-start="18653" data-end="18793"&gt;&lt;strong data-start="18653" data-end="18793"&gt;A vehicle that can sense what is happening, understand the situation, identify when something is wrong, and take the appropriate action.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-start="18795" data-end="18882" data-is-last-node="" data-is-only-node=""&gt;&lt;strong data-start="18795" data-end="18882" data-is-last-node=""&gt;The two systems are now talking.&lt;br data-start="18829" data-end="18832" /&gt; The next step is to put them inside the vehicle.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Github Repo:&lt;a href="https://github.com/ForgedCircuits/EVA-Guardian.git" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;&amp;nbsp;https://github.com/ForgedCircuits/EVA-Guardian.git&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>RE: EVA Guardian[Part_5] - The Integrated System</title><link>https://community.element14.com/thread/237940?ContentTypeID=1</link><pubDate>Mon, 24 Aug 2026 15:49:39 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:b5045b8a-0a3d-4409-b4b3-20717c2f4e03</guid><dc:creator>Sudarshan_Hegde</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237940?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57181/eva-guardian-part_5---the-integrated-system/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&amp;nbsp;&lt;a href="https://community.element14.com/members/sumanth_5f00_m_5f00_n"&gt;Sumanth_m_n&lt;/a&gt;&amp;nbsp;, that looping back technique and finding out the communication error in cable is a very nice idea. Overall intersting topic.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: EVA Guardian[Part_5] - The Integrated System</title><link>https://community.element14.com/thread/237937?ContentTypeID=1</link><pubDate>Mon, 24 Aug 2026 09:05:43 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:1e8ea986-6e38-44c8-a197-41b72c4857bb</guid><dc:creator>Sumanth_m_n</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237937?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57181/eva-guardian-part_5---the-integrated-system/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;An additional fault detection method that I find particularly compelling involves the implementation of a voltage measurement system across various nodes. While this system would be compact and simple, it would significantly enhance a long-term system analysis capabilities.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: EVA Guardian[Part_5] - The Integrated System</title><link>https://community.element14.com/thread/237936?ContentTypeID=1</link><pubDate>Mon, 24 Aug 2026 09:03:22 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:ddbe646e-37aa-4296-a2c1-ca525f36a247</guid><dc:creator>Sumanth_m_n</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/237936?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57181/eva-guardian-part_5---the-integrated-system/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span style="font-family:inherit;"&gt;Yes,&amp;nbsp;&lt;a href="https://community.element14.com/members/dab"&gt;DAB&lt;/a&gt;&amp;nbsp;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A communication fault represents the most critical vulnerability within any multi-node system.&lt;/p&gt;
&lt;p&gt;Given the availability of the self loopback feature on this RS485 transceiver, accessible via jumpers, I was able to successfully implement this functionality.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Hall-w-EV - Post 3 - Joystick control</title><link>https://community.element14.com/thread/237935?ContentTypeID=1</link><pubDate>Mon, 24 Aug 2026 04:37:57 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:f8493e60-f10d-434d-942b-97fae5efb1ae</guid><dc:creator>hambreros</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237935?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57174/hall-w-ev---post-3---joystick-control/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;thanks - yeah it was cool that from a basic 1 servo spins, to a robot that moves around was not that much work - just 2 servos spins but at the right rate - the fact that the servos are identical and one is on the opposite side so turning backwards through us a little bit&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Hall-w-EV - Post 3 - Joystick control</title><link>https://community.element14.com/thread/57174?ContentTypeID=0</link><pubDate>Thu, 13 Aug 2026 13:56:38 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:e1fea7bd-9829-42ec-a09e-5f70469ffc3a</guid><dc:creator>tamadillo</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57174?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57174/hall-w-ev---post-3---joystick-control/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Hi again, it&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;. Last post the robot&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57173/hall-w-ev---post-2---giving-the-robot-a-voice" data-e14adj="t"&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;learned to make noise&lt;/a&gt;. This one is smaller but makes the whole thing way more fun to actually drive: a real joystick &amp;mdash; drag it with a mouse or thumb, or just use&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;WASD&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/ vim-style&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;hjkl&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on a keyboard &amp;mdash; instead of wrestling two separate wheel sliders at once.&lt;/p&gt;
&lt;h2 id="what-weve-actually-built"&gt;What we&amp;rsquo;ve actually built&lt;/h2&gt;
&lt;p&gt;Since&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57131/hall-w-ev-post-1---the-wheels-are-turning-mostly" data-e14adj="t"&gt;Post 1&lt;/a&gt;, driving meant dragging&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;two&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;independent vertical sliders. Well actually up till now we haven&amp;rsquo;t connected both servos but yeay a slider per wheel like driving a tank, and how do you even control 2 controls with 1 mouse? So the control page now has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An on-screen joystick pad &amp;mdash; drag the stick in any direction, let go and it springs back to center and stops, same &amp;ldquo;throttle stick, not a light switch&amp;rdquo; feel as the wheel sliders had.&lt;/li&gt;
&lt;li&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;WASD&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and vim&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;hjkl&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;both drive the same stick &amp;mdash; whichever one you reach for first works, and they combine, so forward + turn gives you a proper diagonal instead of a hard pivot.&lt;/li&gt;
&lt;li&gt;The old per-wheel sliders are still there underneath, now relabeled &amp;ldquo;Manual Wheel Control&amp;rdquo; &amp;mdash; occasionally useful for trimming one wheel on its own, but the joystick is the one you actually want to drive with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_02_5F00_joystick_5F00_controls.gif" /&gt;&lt;/p&gt;
&lt;h2 id="keeping-the-mixing-in-one-place"&gt;Keeping the mixing in one place&lt;/h2&gt;
&lt;p&gt;The tempting shortcut was to do the &amp;ldquo;turn this drag angle into two wheel speeds&amp;rdquo; math in JavaScript and post straight to the existing&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;/api/wheel/&amp;lt;n&amp;gt;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;endpoint per wheel. We didn&amp;rsquo;t do that &amp;mdash; the frontend has no business knowing how many wheels this thing has or how they&amp;rsquo;re mixed. Instead the page posts one thing,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;{x, y}&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(turn, throttle, both -100..100), to a new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;POST /api/drive&lt;/code&gt;, and the actual arcade-mixing math lives entirely on the Python side:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:06978c4b-e36a-4c2c-8bec-4a724181f156:type=c_cpp&amp;text=def%20mix_drive%28x%2C%20y%29%3A%0A%20%20%20%20x%20%3D%20max%28-100%2C%20min%28100%2C%20int%28x%29%29%29%0A%20%20%20%20y%20%3D%20max%28-100%2C%20min%28100%2C%20int%28y%29%29%29%0A%20%20%20%20return%20max%28-100%2C%20min%28100%2C%20y%20%2B%20x%29%29%2C%20max%28-100%2C%20min%28100%2C%20y%20-%20x%29%29]&lt;/p&gt;
&lt;p&gt;One function, one place that knows&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;wheel1 = throttle + turn&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;wheel2 = throttle - turn&lt;/code&gt;. If we ever add a third wheel, a different chassis, or want to curve the turn response, that&amp;rsquo;s a one-function change, not a hunt through frontend code.&lt;/p&gt;
&lt;h2 id="mirrored-servos-mirrored-bug"&gt;Mirrored servos, mirrored bug&lt;/h2&gt;
&lt;p&gt;Software done, so time to actually push the stick forward with both wheels connected at once &amp;mdash; first time we&amp;rsquo;d had them both hooked up and driven together rather than one at a time. Robot spun in place instead of driving forward. Wheel 1 was doing exactly what it should; wheel 2 was going backward.&lt;/p&gt;
&lt;p&gt;Both servos are the same part, wired the same way, running the same firmware &amp;mdash; but they&amp;rsquo;re bolted to opposite sides of the chassis, mirror image of each other, the same way your left shoe and right shoe are mirror images built from the same last. &amp;ldquo;Spin clockwise&amp;rdquo; looks like forward from one side and backward from the other, so the exact same pulse width that drove wheel 1 forward drove wheel 2 in reverse. Nothing wrong with the mixing math from the last section &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;mix_drive()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;was handing out perfectly correct forward speeds for both wheels, it&amp;rsquo;s just that one wheel&amp;rsquo;s servo interprets &amp;ldquo;forward&amp;rdquo; backwards from the other.&lt;/p&gt;
&lt;p&gt;Fixed it at the one point in the firmware that turns a commanded speed into an actual pulse, not by touching the mixing math or anything upstream of it:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:d47811d9-5485-43f0-89a7-52476c0b82a9:type=c_cpp&amp;text=%23define%20WHEEL1_REVERSED%20false%0A%23define%20WHEEL2_REVERSED%20true%0A...%0AservoFrame%28SERVO1_PIN%2C%20speedToPulseUs%28WHEEL1_REVERSED%20%3F%20-wheel1Speed%20%3A%20wheel1Speed%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20SERVO2_PIN%2C%20speedToPulseUs%28WHEEL2_REVERSED%20%3F%20-wheel2Speed%20%3A%20wheel2Speed%29%29%3B]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;wheel1Speed&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;wheel2Speed&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;themselves &amp;mdash; the values the Bridge handlers store, the values the Python side and the joystick&amp;rsquo;s arcade mixing both reason about &amp;mdash; still mean &amp;ldquo;positive is forward&amp;rdquo; for both wheels. The mirroring correction is a single negation right at the pulse-generation step, isolated to the one wheel that&amp;rsquo;s actually mounted backwards. If it turns out a future chassis needs the other wheel flipped too (or flipped back), it&amp;rsquo;s a one-line change, not a rethink of the mixing.&lt;/p&gt;
&lt;h2 id="a-real-off-switch"&gt;A real OFF switch&lt;/h2&gt;
&lt;p&gt;With the direction sorted, one servo was still making a faint noise even sitting at commanded speed 0 &amp;mdash; the same self-correcting buzz&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57131/hall-w-ev-post-1---the-wheels-are-turning-mostly" data-e14adj="t"&gt;Post 1&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;first ran into, just quieter now that both servos are trimmed better. Trimming the pot gets you close to the servo&amp;rsquo;s true center, not exactly onto it, and a held 1500us &amp;ldquo;stop&amp;rdquo; pulse still gives the servo&amp;rsquo;s internal position-holding loop a target to compare itself against. Close-but-not-perfect is still enough for it to keep nudging.&lt;/p&gt;
&lt;p&gt;So instead of chasing the trim pot further, we added a real motor power toggle &amp;mdash; a button per wheel that does something a commanded speed of 0 can&amp;rsquo;t: stop sending that servo a pulse train&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;at all&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:8ca6e1ea-33ed-4b4c-83c7-2b7d2dad2fde:type=c_cpp&amp;text=static%20void%20servoFrame%28int%20pin1%2C%20unsigned%20int%20pulse1Us%2C%20bool%20enable1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20pin2%2C%20unsigned%20int%20pulse2Us%2C%20bool%20enable2%29%20%7B%0A%20%20%20%20if%20%28enable1%29%20%7B%0A%20%20%20%20%20%20%20%20digitalWrite%28pin1%2C%20HIGH%29%3B%0A%20%20%20%20%20%20%20%20delayMicroseconds%28pulse1Us%29%3B%0A%20%20%20%20%20%20%20%20digitalWrite%28pin1%2C%20LOW%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20%2F%2F%20...same%20for%20pin2%2Fenable2%0A%7D]&lt;/p&gt;
&lt;p&gt;No pulse means nothing for the internal loop to react to &amp;mdash; quieter than any stop pulse we could trim to, held or not.&lt;/p&gt;
&lt;p&gt;Worth being upfront about what this isn&amp;rsquo;t: it&amp;rsquo;s not a real power switch. The board only ever drove the servo&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;signal&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;line &amp;mdash; the 5V rail has always come straight off the shared supply with no relay or MOSFET in between , so &amp;ldquo;motor off&amp;rdquo; here can&amp;rsquo;t cut actual voltage to the servo. That would need new hardware &amp;mdash; a MOSFET or relay switched from a spare GPIO &amp;mdash; not just a firmware change, so we deliberately scoped this to the signal-only version rather than reaching for a soldering iron mid-feature. Given the noise was coming from the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;signal&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;being held near-but-not-quite-center rather than from anything drawing power at true idle, it&amp;rsquo;s also very likely the actual fix for the buzz, not just a consolation prize.&lt;/p&gt;
&lt;h2 id="holding-a-key-isnt-a-real-browser-event"&gt;&amp;ldquo;Holding a key&amp;rdquo; isn&amp;rsquo;t a real browser event&lt;/h2&gt;
&lt;p&gt;First pass: listen for&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keydown&lt;/code&gt;, send the drive command once. Worked for about half a second &amp;mdash; press&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;w&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and the robot lurches forward, then stops on its own even though the key&amp;rsquo;s still very much held down.&lt;/p&gt;
&lt;p&gt;Turns out&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keydown&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;fires once per press, and after that the browser&amp;rsquo;s own key-repeat kicks in &amp;mdash; which is inconsistent across OSes, has a noticeable initial delay, and isn&amp;rsquo;t something we should be relying on for &amp;ldquo;keep the motor running.&amp;rdquo; Worse:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57131/hall-w-ev-post-1---the-wheels-are-turning-mostly" data-e14adj="t"&gt;Post 1&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;built a 1-second watchdog into the STM32 side specifically so a dropped connection stops the wheels instead of leaving them spinning &amp;mdash; and a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keydown&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that fires once and then goes quiet for a while looks exactly like a dropped connection to that watchdog.&lt;/p&gt;
&lt;p&gt;Fix was the same pattern the wheel sliders already used for drag events, just driven by a timer instead of input events &amp;mdash; track which keys are currently down in a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Set&lt;/code&gt;, and re-send the current vector on a plain interval for as long as any of them are held:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:44a77e00-8e0e-4f9f-b833-020571a160b2:type=c_cpp&amp;text=setInterval%28%28%29%20%3D%3E%20%7B%0A%20%20if%20%28keyboardDriving%29%20drive%28...keyboardVector%28%29%2C%20false%29%3B%0A%7D%2C%20SEND_INTERVAL_MS%29%3B]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keydown&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;keyup&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;just add/remove from the set; the interval is what actually keeps commands flowing often enough to stay ahead of the watchdog.&lt;/p&gt;
&lt;h2 id="the-keyup-that-never-comes"&gt;The keyup that never comes&lt;/h2&gt;
&lt;p&gt;Second gotcha, found by alt-tabbing away mid-drive without letting go of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;w&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;first: the robot kept driving.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keyup&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;only fires if the browser is still the one listening &amp;mdash; alt-tab, clicking outside the page, anything that steals focus, and the browser just stops delivering key events altogether. No&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;keyup&lt;/code&gt;, so our held-keys set never clears.&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:2d5b2cdf-8311-4451-9f40-22fd9236615d:type=javascript&amp;text=window.addEventListener%28%27blur%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20if%20%28pressedKeys.size%20%3D%3D%3D%200%29%20return%3B%0A%20%20pressedKeys.clear%28%29%3B%0A%20%20keyboardDriving%20%3D%20false%3B%0A%20%20drive%280%2C%200%2C%20true%29%3B%0A%7D%29%3B]&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Losing focus now stops the robot immediately instead of waiting out the watchdog&amp;rsquo;s full second &amp;mdash; which, at &amp;ldquo;robot with wheels in a hallway,&amp;rdquo; felt like the actually-important version of this bug, not just a nice-to-have.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/20260813_5F00_02_5F00_demo_5F00_joystick_5F00_control_5F00_web.mp4:640:360]&lt;/span&gt;&lt;/p&gt;
&lt;div class="post-content"&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;The joystick makes driving nicer, but you&amp;rsquo;re still driving blind &amp;mdash; next up is the camera, so this actually becomes the &amp;ldquo;puppy on a leash from your phone&amp;rdquo; post the original plan promised.&lt;/p&gt;
&lt;h2 id="the-codes"&gt;The codes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tamadillo/hall-w-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/tamadillo/hall-w-EV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;)&lt;/p&gt;
&lt;/div&gt;</description></item><item><title>Hall-w-EV - Post 2 - Giving the robot a voice</title><link>https://community.element14.com/thread/57173?ContentTypeID=0</link><pubDate>Thu, 13 Aug 2026 04:28:09 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:52944598-6fda-44ae-8a96-11aa9663471b</guid><dc:creator>tamadillo</dc:creator><slash:comments>5</slash:comments><comments>https://community.element14.com/thread/57173?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57173/hall-w-ev---post-2---giving-the-robot-a-voice/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Hi again, it&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;. Last post the robot learned to roll&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57131/hall-w-ev-post-1---the-wheels-are-turning-mostly" data-e14adj="t"&gt;Post 1 - The wheels are turning (mostly)&lt;/a&gt;. This post it learned to make noise &amp;mdash; sound effects, an air raid siren, an announcement system, and it can even talk now. Getting there was way more of an adventure than the wheels were, mostly because the bug wasn&amp;rsquo;t actually a bug, it was a whole container we didn&amp;rsquo;t know existed.&lt;/p&gt;
&lt;h2 id="what-weve-actually-built"&gt;What we&amp;rsquo;ve actually built&lt;/h2&gt;
&lt;p&gt;The control web page now has a &amp;ldquo;Sound System&amp;rdquo; panel underneath the wheel controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A grid of sound effects like laser blasts and guns reloading&lt;/li&gt;
&lt;li&gt;An&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Air Raid Siren&lt;/strong&gt;, and an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;AusAlert&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;tone (853Hz and 960Hz played together) inspired by Australia&amp;rsquo;s emergency phone-alert system&lt;/li&gt;
&lt;li&gt;Text-to-speech (TTS) announcements &amp;mdash; inspired by &amp;ldquo;Giant Voice&amp;rdquo; systems we&amp;rsquo;ve seen in videos from Middle East conflict zones and school lockdown drills, telling you to &amp;ldquo;shelter in place&amp;rdquo;. Partly inspired by the new Arduino App Lab update, which mentions TTS bricks.&lt;/li&gt;
&lt;li&gt;A volume slider, and a big&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;SHUT UP&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;button, because once you give a robot a siren you will absolutely need a way to make it stop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And most recently: cruising music, so it can play a track while it drives around. More on why that one was trickier than it sounds in a minute.&lt;/p&gt;
&lt;h2 id="hardware-distraction"&gt;Hardware distraction&lt;/h2&gt;
&lt;p&gt;The Braitenberg vehicle chassis we&amp;rsquo;re using is inherently unstable. Two wheels and a stopper meant it would often tip during testing. Inspired by the pantographs on trains that pass our back yard, as well as drones that can land and stick to a moving object, we tried to build a self-correcting leg system for our EV.&lt;/p&gt;
&lt;div style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:960px;"&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_tram_5F00_pantograph_5F00_02.gif" /&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_tram_5F00_pantograph_5F00_01.gif" /&gt;&lt;/div&gt;
&lt;div style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;"&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/20260813_5F00_drone_5F00_land_5F00_and_5F00_cling_5F00_web.mp4:640:360]&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.instagram.com/p/DboDwK_gXT7" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://www.instagram.com/p/DboDwK_gXT7&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;credit&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://www.instagram.com/zaruba.tech/" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Zaruba&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This took a bunch of experimenting and time. In the end the results weren&amp;rsquo;t that good, and we realised that once we finally attached a camera to the setup, we&amp;rsquo;d also need to compensate and auto-correct the camera to point at the horizon.&lt;/p&gt;
&lt;p&gt;&lt;img style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:960px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_adjustable_5F00_leg_5F00_combined.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In the end we dropped the idea and moved on with a couple of wooden blocks.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:960px;max-width:960px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_block_5F00_legs.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="tts-no-work"&gt;TTS no work&lt;/h2&gt;
&lt;p&gt;As the UNO Q updated to the latest firmware of Arduino App Lab, we got briefly excited that there might also be a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;neural&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;text-to-speech option built in (an actual AI voice model instead of the classic robot monotone) &amp;mdash; and there is one,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;arduino:tts&lt;/code&gt;. Got all the way to testing it before finding out it&amp;rsquo;s built specifically for the new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Ventuno Q&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;board and not supported by the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;UNO Q&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Turns out Arduino just announced the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/products/arduino/b/blog/posts/arduino-ventuno" data-e14adj="t"&gt;VENTUNO Q&lt;/a&gt;. Our UNO Q has 2GB of RAM and no AI chip. VENTUNO Q packs a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Qualcomm Dragonwing IQ‑8275&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; a proper 40 TOPS neural processor &amp;mdash; plus 16GB of RAM, specifically so it can run real local AI: computer vision,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/products/roadtest/rt/roadtests/722/roadtest-open-call?CommentId=63a8ef97-9664-436f-a11a-6178ed0f25c8" data-e14adj="t"&gt;offline AI assistants running local speech models&lt;/a&gt;, that kind of thing. The neural TTS brick we found needs that NPU to run at all, so on our board it was never going to work &amp;mdash; not a bug, just the wrong hardware for the job. There are a couple of overview videos from embedded world if you want to see it in action:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://www.youtube.com/watch?v=gVd1qKlfCyY" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;developer walkthrough&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://www.youtube.com/watch?v=5wYzlrZVPXY" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;demo reel&lt;/a&gt;. But let&amp;rsquo;s not let the inspiration of TTS go to waste, after scrounging around we worked out we can use the unix&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;espeak-ng&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for the talking instead.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s where it got interesting. The siren and the AusAlert tone worked first try. Wav/MP3 Sound effects and text-to-speech? Dead silent. No errors, no sound, nothing.&lt;/p&gt;
&lt;p&gt;First theory: volume. Turned out to be half right &amp;mdash; the board&amp;rsquo;s speaker volume genuinely was too low by default, and cranking it with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;amixer&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;fixed&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;some&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;of it. But effects and TTS still didn&amp;rsquo;t work, even after that.&lt;/p&gt;
&lt;p&gt;Second theory, once we actually looked: the programs we needed (&lt;code class="language-plaintext highlighter-rouge"&gt;mpg123&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for mp3s,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;espeak-ng&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for the talking) just weren&amp;rsquo;t installed. Fair enough, we thought &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;sudo apt-get install&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;them and done. Except we&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;did&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;which mpg123&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on the board clearly showed it existed. So why couldn&amp;rsquo;t our own code find it?&lt;/p&gt;
&lt;p&gt;Turns out: the robot&amp;rsquo;s Python code doesn&amp;rsquo;t actually run directly on the board&amp;rsquo;s Linux. It runs&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;inside a Docker container&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; basically a little sealed box with its own separate copy of everything. We&amp;rsquo;d installed the programs onto the board itself, not into the box our code was actually running in. Two completely different places, both called &amp;ldquo;the board&amp;rdquo; if you&amp;rsquo;re not paying attention. Once we&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;docker exec&lt;/code&gt;&amp;lsquo;d into the actual container and installed things there instead, everything clicked into place &amp;mdash; almost. Even&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;that&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;had one more gotcha: the container&amp;rsquo;s default user isn&amp;rsquo;t allowed to install anything (&lt;code class="language-plaintext highlighter-rouge"&gt;Permission denied&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on a folder called&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;apt/lists/partial&lt;/code&gt;, if you&amp;rsquo;re curious), so it needed:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:c90c6dd6-bc9d-4d5c-8a83-32b1a234feaf:type=batchfile&amp;text=docker%20exec%20-u%20root%20hall-w-ev-main-1%20apt-get%20install%20-y%20espeak-ng]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;-u root&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;= &amp;ldquo;no really, let me actually install this.&amp;rdquo; Small thing, cost us an hour.&lt;/p&gt;
&lt;h2 id="finding-the-good-stuff"&gt;Finding the good stuff&lt;/h2&gt;
&lt;p&gt;While we were down in that container digging around, we found something way better than what we were looking for: Arduino ships their own official audio tools baked right in &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;arduino.app_peripherals.speaker.Speaker&lt;/code&gt;. It&amp;rsquo;s a proper Python class for playing sound directly, no external programs needed at all.&lt;/p&gt;
&lt;p&gt;As mentioned above, the VENTUNO Q&amp;rsquo;s TTS brick was kind of cool to stumble into by accident while debugging a sound effect &amp;mdash; but a dead end for now. We&amp;rsquo;re sticking with the classic robot voice, which honestly suits an emergency-siren robot better anyway.&lt;/p&gt;
&lt;p&gt;Since the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/arduino/app-bricks-py/tree/main/src/arduino/app_bricks/tts" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;TTS brick&amp;rsquo;s code is public&lt;/a&gt;, we went and actually read it out of curiosity, and it turns out it does basically the same chunk-and-check-cancelled trick we were about to build by hand, just with a lot more going on underneath:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn&amp;rsquo;t run the AI voice model in the same program at all &amp;mdash; it makes a network request to a separate always-on service and streams the audio&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;back&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;as the model generates it, piece by piece, instead of waiting for the whole sentence to finish.&lt;/li&gt;
&lt;li&gt;Long text gets split at up to 1024 characters, cut on the last&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;.&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;!&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;?&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;it can find before the limit, so it doesn&amp;rsquo;t chop a sentence in half &amp;mdash; smarter than our &amp;ldquo;just cut it off at 300 characters and hope.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Cancelling has to happen in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;two&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;places: locally (stop feeding audio to the speaker) and remotely (tell the AI service currently mid-sentence over the network to actually stop generating).&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s even a &amp;ldquo;warmup&amp;rdquo; &amp;mdash; the instant it starts up, it quietly synthesizes the word &amp;ldquo;ok&amp;rdquo; to itself, just so the neural network is already loaded by the time you need it for real, instead of your first sentence being the slow one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kind of validating, honestly. The &amp;ldquo;cut it into pieces, check a flag between each one&amp;rdquo; idea wasn&amp;rsquo;t a hack we made up &amp;mdash; it&amp;rsquo;s the same shape of solution the actual Arduino engineers reached for. Theirs just has a neural network and a network request bolted on the front of it.&lt;/p&gt;
&lt;p&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Speaker&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;class was the real win though. The simple way to use it plays a whole sound start-to-finish with no way to interrupt it &amp;mdash; fine for a short laser blast, useless for &amp;ldquo;stop the siren right now.&amp;rdquo; So instead we feed it small chunks (a tenth of a second each) in a loop, and check &amp;ldquo;should I stop?&amp;rdquo; between every single chunk:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:b8a4db98-573d-4870-99c3-ec3c00aabef5:type=c_cpp&amp;text=def%20_stream_pcm%28speaker%2C%20sample_rate%2C%20channels%2C%20samples%2C%20stop_event%29%3A%0A%20%20%20%20chunk_len%20%3D%20max%281%2C%20int%28sample_rate%20%2A%200.1%29%29%20%2A%20channels%0A%20%20%20%20for%20i%20in%20range%280%2C%20len%28samples%29%2C%20chunk_len%29%3A%0A%20%20%20%20%20%20%20%20if%20stop_event.is_set%28%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20speaker.play%28samples%5Bi%3Ai%20%2B%20chunk_len%5D%29]&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the whole trick behind the SHUT UP button, and behind &amp;ldquo;press the siren again while it&amp;rsquo;s already going&amp;rdquo; restarting it cleanly instead of two sirens fighting each other.&lt;/p&gt;
&lt;h2 id="making-espeak-ng-actually-stick-around"&gt;Making espeak-ng actually stick around&lt;/h2&gt;
&lt;p&gt;Remember that&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;docker exec -u root ... apt-get install espeak-ng&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;fix from earlier? It worked great &amp;mdash; for one restart. Then we rebooted the board again and it was just gone. Turns out installing something into a running container by hand doesn&amp;rsquo;t actually stick &amp;mdash; the container gets rebuilt from scratch every time you redeploy, and &amp;ldquo;by hand&amp;rdquo; doesn&amp;rsquo;t survive being rebuilt. Cool, so our talking robot&amp;rsquo;s voice box was actually a ticking time bomb this whole time.&lt;/p&gt;
&lt;p&gt;We did not want to just re-run that command forever every time we updated the code. So: real fix time.&lt;/p&gt;
&lt;p&gt;We remembered seeing a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;brick_compose.yaml&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;mentioned in Arduino&amp;rsquo;s own code while we were poking around earlier, and it turns out there&amp;rsquo;s a whole&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://blog.arduino.cc/2026/04/29/arduino-app-lab-0-7-custom-bricks-are-here/" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;custom bricks feature&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for exactly this &amp;mdash; you can package up your own little service, container and all, as part of your app. Every example of it we could find online only used pre-built images though, never a Dockerfile you write yourself, so we genuinely didn&amp;rsquo;t know if that part actually worked or if we&amp;rsquo;d be wasting an evening.&lt;/p&gt;
&lt;p&gt;Quick test first: a throwaway folder with just a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Dockerfile&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that installs espeak-ng and then does nothing (&lt;code class="language-plaintext highlighter-rouge"&gt;sleep infinity&lt;/code&gt;), wired up as a brick. Restarted the app to see what would happen.&lt;/p&gt;
&lt;p&gt;It built the Dockerfile. For real. Docker log spam and everything, right there in the deploy output &amp;mdash; our own robot, building its own container image, from a text file we wrote, installing a package with full root access and zero permission drama, because this time it&amp;rsquo;s happening at&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;build&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;time, not sneaking in through&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;docker exec&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;afterward.&lt;/p&gt;
&lt;p&gt;So we built the actual thing: a tiny container that does nothing but run espeak-ng behind a dead-simple web server &amp;mdash;&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:37fa5c2a-0e2e-48b3-b1a6-22aded524273:type=c_cpp&amp;text=%23%20POST%20%2Fsynthesize%20%7B%22text%22%3A%20%22...%22%2C%20%22voice%22%3A%20%22en-us%2Bm3%22%2C%20%22speed%22%3A%20150%7D%0Aresult%20%3D%20subprocess.run%28%0A%20%20%20%20%5B%27espeak-ng%27%2C%20%27--stdout%27%2C%20%27-v%27%2C%20voice%2C%20%27-s%27%2C%20speed%2C%20text%5D%2C%0A%20%20%20%20capture_output%3DTrue%2C%20timeout%3D10%2C%0A%29%0A%23%20...and%20send%20result.stdout%20back%20as%20the%20response%20body]&lt;/p&gt;
&lt;p&gt;&amp;mdash; and now the main robot code just sends it a sentence over the network and gets a WAV file back, instead of running espeak-ng itself. Since the whole container gets rebuilt from that same Dockerfile every single time we deploy, there&amp;rsquo;s nothing left to mysteriously vanish. Tested it properly too &amp;mdash; full restart, both containers rebuilt from nothing, and the robot could talk again immediately, no manual fixing required. That&amp;rsquo;s the actual fix, not a &amp;ldquo;seems fine for now.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="cruising-music-without-a-50mb-file"&gt;Cruising music (without a 50MB file)&lt;/h2&gt;
&lt;p&gt;Last thing: we wanted the robot to play a music track while driving around &amp;mdash; cruising music. Obvious approach: convert the song to the same format as the sound effects. Except the effects are only a few seconds long, and this song is almost 5 minutes &amp;mdash; converted the &amp;ldquo;simple&amp;rdquo; way, it would&amp;rsquo;ve turned a 7MB mp3 into something like 50MB sitting on the robot for no reason.&lt;/p&gt;
&lt;p&gt;So instead of converting the whole song upfront, it gets decoded a tiny piece at a time, right as it&amp;rsquo;s needed, and each piece goes straight into the same chunk-player from before. The song is never sitting fully unpacked in memory or on disk, and we get to reuse all the cancel/restart logic we&amp;rsquo;d already built.&lt;/p&gt;
&lt;p&gt;the sound board controls&lt;/p&gt;
&lt;p&gt;&lt;img style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:960px;max-width:960px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/pastedimage1786625900480v1.png" /&gt;&lt;/p&gt;
&lt;div style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:960px;max-width:960px;"&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/20260813_5F00_demo_5F00_hall_2D00_w_2D00_EV_5F00_sound_5F00_board.mp4:640:360]&lt;/div&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Camera + remote driving&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; the &amp;ldquo;puppy on a leash from your phone&amp;rdquo; post&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-codes"&gt;The codes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tamadillo/hall-w-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/tamadillo/hall-w-EV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;mdash;&lt;span&gt;&amp;nbsp;[mention:73a531dd49a044558f94d68fa340763b:e9ed411860ed4f2ba0265705b8793d05]&amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(and&lt;span&gt;&amp;nbsp;[mention:0ede6c1c051849c0a39ea19c96a98013:e9ed411860ed4f2ba0265705b8793d05]&amp;nbsp;&lt;/span&gt;)&lt;/p&gt;</description></item><item><title>RE: Hall-w-EV - Post 2 - Giving the robot a voice</title><link>https://community.element14.com/thread/237934?ContentTypeID=1</link><pubDate>Mon, 24 Aug 2026 04:36:21 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:9c832cdc-cdf4-4d62-889a-5e5e11abc6a8</guid><dc:creator>hambreros</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237934?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57173/hall-w-ev---post-2---giving-the-robot-a-voice/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;they did get stuck a little on the floor boards but OK, we also got some wheel slippage, we had covered the wooden wheels with some old bike tube rubber but still got some slippage like I noticed you mentioned in&amp;nbsp;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57203/dockbot---part-6---the-mostly-open-closed-loop-control" data-e14adj="t"&gt;community.element14.com/.../dockbot---part-6---the-mostly-open-closed-loop-control&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: hall-w-EV - Post 4 - Giving it eyes</title><link>https://community.element14.com/thread/237931?ContentTypeID=1</link><pubDate>Sun, 23 Aug 2026 23:21:54 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:74963af3-abb9-4769-a7d7-2b39b5ee31f3</guid><dc:creator>tamadillo</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237931?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57184/hall-w-ev---post-4---giving-it-eyes/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;thanks - it is so cool that you can plug a camera in and next thing you have a FPV remote control car&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>hall-w-EV - Post 4 - Giving it eyes</title><link>https://community.element14.com/thread/57184?ContentTypeID=0</link><pubDate>Sun, 16 Aug 2026 12:23:30 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:025465f3-6ae5-4863-bfed-7977041a8582</guid><dc:creator>tamadillo</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57184?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57184/hall-w-ev---post-4---giving-it-eyes/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Hi again, it&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;. Last post ended with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57174/hall-w-ev---post-3---joystick-control" data-e14adj="t"&gt;a promise&lt;/a&gt;: the joystick makes driving nicer, but you&amp;rsquo;re still driving blind. So a USB webcam went on, and the control page now shows what the robot sees.&lt;/p&gt;
&lt;h2 id="what-weve-actually-built"&gt;What we&amp;rsquo;ve actually built&lt;/h2&gt;
&lt;p&gt;A live video feed right on the same page as the joystick &amp;mdash; the &amp;ldquo;puppy on a leash from your phone&amp;rdquo; post the original plan called for, minus the actual leash. Point the robot somewhere without needing to be in the room with it.&lt;/p&gt;
&lt;h2 id="checking-what-the-framework-already-gives-you"&gt;Checking what the framework already gives you&lt;/h2&gt;
&lt;p&gt;Same move as the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57173/hall-w-ev---post-2---giving-the-robot-a-voice" data-e14adj="t"&gt;TTS detour&lt;/a&gt;: before writing any camera code, went and read&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/arduino/app-bricks-py" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;arduino/app-bricks-py&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to see what Arduino already ships. Turned out&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;app_peripherals/camera&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a whole unified abstraction &amp;mdash; one&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;class covering CSI, USB (V4L), IP, and even WebSocket sources, same family as the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Speaker&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;peripheral the sound system already uses:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:ef2ef448-3ffa-4fb5-bcbc-f4e48e522ac4:type=python&amp;text=from%20arduino.app_peripherals.camera%20import%20Camera%0A%0Acamera%20%3D%20Camera%28%22usb%3A0%22%2C%20resolution%3D%28640%2C%20480%29%2C%20fps%3D15%29%0Acamera.start%28%29%0Aframe%20%3D%20camera.capture%28%29%20%20%20%23%20numpy%20array%2C%20or%20None%0A%23%20or%3A%20for%20frame%20in%20camera.stream%28%29%3A%20...]&lt;/p&gt;
&lt;p&gt;Since&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Speaker&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;was already confirmed bundled in this app&amp;rsquo;s base container with zero extra install, betting&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is too &amp;mdash; no new brick, no sidecar container, just a normal peripheral call from our own code. The only actual new dependency is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;opencv-python-headless&lt;/code&gt;, for turning each frame into a JPEG.&lt;/p&gt;
&lt;h2 id="the-dumbest-frontend-that-works"&gt;The dumbest frontend that works&lt;/h2&gt;
&lt;p&gt;The tempting-but-overbuilt version of this involves a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, a WebSocket, and a JS render loop pulling frames off it. Skipped all of that:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:515c29f5-6d50-41a6-9ca3-935666d372e0:type=html&amp;text=%3Cimg%20id%3D%22cameraFeed%22%20src%3D%22%2Fapi%2Fcamera%2Fstream%22%20alt%3D%22Live%20camera%20feed%22%3E]&lt;/p&gt;
&lt;p&gt;&lt;span&gt;That&amp;rsquo;s the entire client-side video pipeline.&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;GET /api/camera/stream&lt;/code&gt;&lt;span&gt;&amp;nbsp;returns&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;multipart/x-mixed-replace&lt;/code&gt;&lt;span&gt;&amp;nbsp;&amp;mdash; a boundary-delimited stream of JPEG frames &amp;mdash; and browsers have known how to render that straight into an&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;span&gt;&amp;nbsp;tag since basically forever. No JS needed for the video itself, just a listener on the&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;error&lt;/code&gt;&lt;span&gt;&amp;nbsp;event for when there&amp;rsquo;s no camera to show:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:cc9ee16d-5887-4699-b0d5-15f1c32a49c1:type=javascript&amp;text=cameraFeed.addEventListener%28%27error%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20cameraFeed.style.display%20%3D%20%27none%27%3B%0A%20%20fetch%28%27%2Fapi%2Fcamera%2Fstatus%27%29.then%28r%20%3D%3E%20r.json%28%29%29.then%28data%20%3D%3E%20%7B%0A%20%20%20%20cameraError.hidden%20%3D%20false%3B%0A%20%20%20%20cameraError.textContent%20%3D%20data.error%20%7C%7C%20%27camera%20stream%20unavailable%27%3B%0A%20%20%7D%29%3B%0A%7D%29%3B]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Same &amp;ldquo;surface the real reason, not a cryptic broken icon&amp;rdquo; instinct as the sound system&amp;rsquo;s error banner from a couple of posts back.&lt;/p&gt;
&lt;h2 id="starting-the-camera-without-blocking-everything-else"&gt;Starting the camera without blocking everything else&lt;/h2&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera.start()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;has its own connection retry loop with exponential backoff &amp;mdash; reasonable for &amp;ldquo;give the USB device a moment to enumerate,&amp;rdquo; bad if it&amp;rsquo;s sitting on the same startup path as the wheel and sound APIs. A slow or missing camera shouldn&amp;rsquo;t hold up driving the robot.&lt;/p&gt;
&lt;p&gt;So&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;python/camera.py&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;kicks off&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera(...).start()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on a background thread at import time, mirroring&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;sounds.py&lt;/code&gt;&amp;rsquo;s general shape:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:8a857f0a-1d9f-411b-b561-fd207e92cf35:type=python&amp;text=def%20_start%28%29%3A%0A%20%20%20%20global%20_camera%2C%20_error%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20cam%20%3D%20Camera%28SOURCE%2C%20resolution%3DRESOLUTION%2C%20fps%3DFPS%29%0A%20%20%20%20%20%20%20%20cam.start%28%29%0A%20%20%20%20%20%20%20%20_camera%20%3D%20cam%0A%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20_error%20%3D%20str%28e%29%0A%0Athreading.Thread%28target%3D_start%2C%20daemon%3DTrue%29.start%28%29]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;status()&lt;/code&gt;&lt;span&gt;&amp;nbsp;reports&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;(ok, error)&lt;/code&gt;&lt;span&gt;&amp;nbsp;off that shared state, and the stream route just checks it before handing back the actual&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;multipart&lt;/code&gt;&lt;span&gt;&amp;nbsp;response:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:bc573021-4957-485a-8705-cd1bdd0613d4:type=python&amp;text=%40%20web.route%28%27%2Fapi%2Fcamera%2Fstream%27%29%20%23%20space%20added%20not%20to%20look%20like%20email%20address%0Adef%20camera_stream%28%29%3A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20which%20flags%20the%20post%20as%20inappropriate%0A%20%20%20%20ok%2C%20error%20%3D%20camera.status%28%29%0A%20%20%20%20if%20not%20ok%3A%0A%20%20%20%20%20%20%20%20return%20jsonify%28%7B%27ok%27%3A%20False%2C%20%27error%27%3A%20error%7D%29%2C%20503%0A%20%20%20%20return%20Response%28camera.mjpeg_frames%28%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mimetype%3D%27multipart%2Fx-mixed-replace%3B%20boundary%3Dframe%27%29]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;mjpeg_frames() itself is a small generator wrapping Camera.stream(), JPEG-encoding (cv2.imencode) each frame as it comes.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/2352.20260816_5F00_hall_2D00_w_2D00_EV_2D00_w_2D00_camera.mp4:640:360]&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;Camera&amp;rsquo;s on, joystick works &amp;mdash; next logical step is doing something with the two together: point-and-drive, or finally trying that on-device object detection now that we know&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;UNO Q&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;supports in it&amp;rsquo;s demo bricks.&lt;/p&gt;
&lt;h2 id="the-codes"&gt;The codes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tamadillo/hall-w-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/tamadillo/hall-w-EV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>hall-w-EV - Post 5 - Chase ball</title><link>https://community.element14.com/thread/57185?ContentTypeID=0</link><pubDate>Sun, 16 Aug 2026 13:29:31 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:379032eb-dc9a-493d-ad2f-f249251e78ab</guid><dc:creator>tamadillo</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57185?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57185/hall-w-ev---post-5---chase-ball/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Hi again, it&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;. Last post&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57184/hall-w-ev---post-4---giving-it-eyes" data-e14adj="t"&gt;gave it eyes&lt;/a&gt;. This one gives it something to do with them: place a tennis ball&amp;nbsp;[emoticon:2aeeb716c0ca40469e4afae40f662cd1]&amp;nbsp;in front of it and the robot drives itself towards it, no hands.&lt;/p&gt;
&lt;h2 id="what-weve-actually-built"&gt;What we&amp;rsquo;ve actually built&lt;/h2&gt;
&lt;p&gt;A&amp;nbsp;[emoticon:2aeeb716c0ca40469e4afae40f662cd1]&amp;nbsp;CHASE&amp;nbsp;toggle next to the joystick. Flip it on, and:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The robot looks for a ball in the camera feed.&lt;/li&gt;
&lt;li&gt;Off-center &amp;rarr; it turns towards it.&lt;/li&gt;
&lt;li&gt;Small (far away) &amp;rarr; it drives forward. Big enough (close) &amp;rarr; it eases off and stops.&lt;/li&gt;
&lt;li&gt;Ball out of frame &amp;rarr; it just stops, same as letting go of the joystick.&lt;/li&gt;
&lt;li&gt;Manual controls (joystick, keyboard, per-wheel sliders) go greyed-out and unresponsive while this is on, so nothing&amp;rsquo;s fighting the robot for the wheel. The ⏻ MOTOR ON/OFF buttons still work regardless &amp;mdash; always an independent kill switch, whoever&amp;rsquo;s driving.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="finding-the-right-brick"&gt;Finding the right brick&lt;/h2&gt;
&lt;p&gt;Same move as every other feature so far: before writing a line of code, went and read&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/arduino/app-bricks-py" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;arduino/app-bricks-py&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to see what already exists. Turns out there&amp;rsquo;s a whole family of vision bricks &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;gesture_recognition&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;mood_detector&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;image_classification&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;object_detection&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;visual_anomaly_detection&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; and the one that actually fits is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;video_objectdetection&lt;/code&gt;: continuous detection off a live camera stream, with per-label callbacks carrying a confidence score and a bounding box.&lt;/p&gt;
&lt;p&gt;Its&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;brick_config.yaml&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;lists:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:c0798960-382b-495c-9b94-dfb21580f6ea:type=python&amp;text=model_by_boards%3A%0A%20%20%20%20-%20platform%3A%20ventunoq%0A%20%20%20%20%20%20model%3A%20yolox-qnn-object-detection%0A%20%20%20%20-%20platform%3A%20unoq%0A%20%20%20%20%20%20model%3A%20yolox-object-detection]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;UNO Q&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; this exact board. Not a VENTUNO-only NPU thing, unlike the neural TTS detour a couple posts back. Genuinely usable here.&lt;/p&gt;
&lt;h2 id="no-training-required--just-check-the-label-list"&gt;No training required &amp;mdash; just check the label list&lt;/h2&gt;
&lt;p&gt;The obvious worry: does a generic pretrained model know what a tennis ball is? Didn&amp;rsquo;t want to assume, so went and checked&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/arduino/app-bricks-py/blob/main/models/models-list.yaml" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;models/models-list.yaml&lt;/code&gt;&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in the same repo before writing any detection code &amp;mdash; it&amp;rsquo;s a YOLOX-Nano model trained on COCO&amp;rsquo;s 80 classes, and the label list includes, verbatim:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:8731bb3f-a577-48c3-aaa0-3d7534130cf0:type=python&amp;text=-%20sports%20ball]&lt;/p&gt;
&lt;p&gt;&lt;span&gt;That&amp;rsquo;s the actual class name (there&amp;rsquo;s no separate &amp;ldquo;tennis ball&amp;rdquo; class in COCO, but &amp;ldquo;sports ball&amp;rdquo; covers it &amp;mdash; it&amp;rsquo;s the canonical example object for that class in the dataset). So: zero custom training, zero Edge Impulse model work. Just register a callback for a class that&amp;rsquo;s already in the box.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:0b40e522-601d-46c4-aa81-b0a4d0be7b68:type=python&amp;text=from%20arduino.app_bricks.video_objectdetection%20import%20VideoObjectDetection%0A%0Adetector%20%3D%20VideoObjectDetection%28camera%3Dshared_camera%2C%20confidence%3D0.5%29%0Adetector.on_detect%28%22sports%20ball%22%2C%20on_ball_detected%29%0Adetector.start%28%29]&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="one-camera-two-features-fighting-over-it"&gt;One camera, two features fighting over it&lt;/h2&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;video_objectdetection&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;wants its own&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to forward frames to the detection sidecar. We already have one open, for&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57184/hall-w-ev---post-4---giving-it-eyes" data-e14adj="t"&gt;last post&amp;rsquo;s live feed&lt;/a&gt;. Tried to hand-wave past this and it immediately mattered:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;claims its physical device the moment it&amp;rsquo;s constructed &amp;mdash; there&amp;rsquo;s an actual registry in the framework&amp;rsquo;s own source specifically so auto-selection doesn&amp;rsquo;t grab something already in use &amp;mdash; so a second, independent&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Camera(&amp;quot;usb:0&amp;quot;, ...)&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;for the same webcam wouldn&amp;rsquo;t just contend for bandwidth, it&amp;rsquo;d fail outright.&lt;/p&gt;
&lt;p&gt;Good thing&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;VideoObjectDetection(camera=...)&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;takes an existing instance instead of always making its own. Added a small accessor to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;camera.py:&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:f939f894-4d61-423d-a4b4-61739e51d40b:type=python&amp;text=def%20get_camera%28%29%3A%0A%20%20%20%20%22%22%22Blocks%20until%20the%20startup%20attempt%20above%20has%20settled%2C%20then%20returns%20the%0A%20%20%20%20shared%20Camera%20instance%20%E2%80%94%20or%20None%20if%20it%20never%20started%20successfully.%22%22%22%0A%20%20%20%20_ready.wait%28%29%0A%20%20%20%20return%20_camera]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;vision.py&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;calls that instead of constructing its own, so both features share the one physical connection to the one webcam instead of racing for it.&lt;/p&gt;
&lt;h2 id="steering-is-just-proportional-control"&gt;Steering is just proportional control&lt;/h2&gt;
&lt;p&gt;No path planning, no PID tuning, nothing fancy &amp;mdash; just &amp;ldquo;how far off-center is it&amp;rdquo; and &amp;ldquo;how big is it,&amp;rdquo; recomputed fresh on every detection message:&lt;/p&gt;
&lt;blockquote style="border-left:4px solid #f2a93b;color:#4a4e4d;font-style:italic;margin-left:0;padding:2px 16px;"&gt;
&lt;p&gt;PID - Proportional Kp, Integral Ki, and Derivative Kd, only recently saw this video from Electronoobs and it looks complicated&amp;nbsp;[View:https://www.youtube.com/watch?v=JFTJ2SS4xyA:640:360]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:4f3480a9-3132-49bc-9030-3be48aae448e:type=python&amp;text=def%20_steer_towards%28bbox%29%3A%0A%20%20%20%20x1%2C%20y1%2C%20x2%2C%20y2%20%3D%20bbox%0A%20%20%20%20frame_w%2C%20frame_h%20%3D%20camera.RESOLUTION%0A%20%20%20%20center_x%20%3D%20%28x1%20%2B%20x2%29%20%2F%202%0A%20%20%20%20box_h%20%20%20%20%3D%20max%281%2C%20y2%20-%20y1%29%0A%0A%20%20%20%20offset%20%20%20%20%20%3D%20%28center_x%20-%20frame_w%20%2F%202%29%20%2F%20%28frame_w%20%2F%202%29%20%20%23%20-1%20..%20%2B1%0A%20%20%20%20size_ratio%20%3D%20box_h%20%2F%20frame_h%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%200%20..%201%0A%0A%20%20%20%20turn%20%20%20%20%20%3D%20max%28-100%2C%20min%28100%2C%20offset%20%2A%20TURN_GAIN%29%29%0A%20%20%20%20throttle%20%3D%20max%280%2C%20min%28MAX_THROTTLE%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%28TARGET_SIZE_RATIO%20-%20size_ratio%29%20%2F%20TARGET_SIZE_RATIO%20%2A%20MAX_THROTTLE%29%29%0A%20%20%20%20return%20turn%2C%20throttle]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;turn&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;throttle&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;go through the exact same&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;mix_drive()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;the joystick posts through from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57174/hall-w-ev---post-3---joystick-control" data-e14adj="t"&gt;two posts ago&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; one function, one place that knows how a turn+throttle pair becomes two wheel speeds, whether a human or a neural network produced them.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;MAX_THROTTLE&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is capped well under full speed on purpose &amp;mdash; this thing drives itself with nothing watching for obstacles, no reason to let it move at joystick speeds.&lt;/p&gt;
&lt;h2 id="what-actually-broke-on-real-hardware"&gt;What actually broke on real hardware&lt;/h2&gt;
&lt;p&gt;First real test went nowhere:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;App.run()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;never scheduled&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;VideoObjectDetection&lt;/code&gt;&amp;rsquo;s background loops, since the brick got built (on its own thread, after&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;App.run()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;had already started) too late for the scheduler to notice &amp;mdash; confirmed by the sidecar sitting there waiting for a connection that never came. Fixed by just running those two loops ourselves in plain daemon threads instead.&lt;/p&gt;
&lt;h2 id="seeing-what-its-actually-seeing"&gt;Seeing what it&amp;rsquo;s actually seeing&lt;/h2&gt;
&lt;p&gt;Once frames were flowing, the ball&amp;rsquo;s box kept flickering against other objects (a bed, a cup) even on a dead-static scene &amp;mdash; turned out to be real per-frame confidence noise, not a &amp;ldquo;one object at a time&amp;rdquo; limitation (it&amp;rsquo;s a genuine multi-object detector, and no, it can&amp;rsquo;t be restricted to only look for balls &amp;mdash; fixed 80-class model, no filter option). Fix: draw a box for everything it sees, not just the ball, and fade them out over a few seconds instead of hard-cutting the instant one frame doesn&amp;rsquo;t reconfirm them.&lt;/p&gt;
&lt;h2 id="the-lost-the-ball-behavior-that-didnt-need-writing"&gt;The &amp;ldquo;lost the ball&amp;rdquo; behavior that didn&amp;rsquo;t need writing&lt;/h2&gt;
&lt;p&gt;Didn&amp;rsquo;t need a lost-ball timeout &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57131/hall-w-ev-post-1---the-wheels-are-turning-mostly" data-e14adj="t"&gt;Post 1&lt;/a&gt;&amp;rsquo;s STM32 watchdog already stops the wheels when commands stop arriving, same as a dropped wifi connection, for free.&lt;/p&gt;
&lt;h2 id="it-worked--and-then-drove-straight-past-the-ball"&gt;It worked &amp;mdash; and then drove straight past the ball&lt;/h2&gt;
&lt;p&gt;Reacting to every single detection message overshot the ball almost every time &amp;mdash; no braking distance. Fixed by pulsing instead: one short move, stop, pause, then decide again from a fresh look &amp;mdash; and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;VideoObjectDetection&lt;/code&gt;&amp;rsquo;s own per-label lock already discards anything that arrives mid-pause, so no new state machine was needed to make that stick.&lt;/p&gt;
&lt;p&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/20260816_5F00_hall_2D00_w_2D00_EV_2D00_chase_2D00_ball.mp4:640:360]&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;Well that&amp;rsquo;s kind of it. Thrilled at how far we got to an actual auto driving EV.&lt;/p&gt;
&lt;h2 id="the-codes"&gt;The codes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tamadillo/hall-w-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/tamadillo/hall-w-EV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/hambreros" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Hambreros&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/tamadillo" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Tamadillo&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;&lt;/code&gt;&lt;/p&gt;</description></item><item><title>RE: hall-w-EV - Post 5 - Chase ball</title><link>https://community.element14.com/thread/237930?ContentTypeID=1</link><pubDate>Sun, 23 Aug 2026 23:20:53 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:8cb2bdaa-424a-4b61-8bd3-05dcf0df313c</guid><dc:creator>tamadillo</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237930?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57185/hall-w-ev---post-5---chase-ball/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;thanks, it was a relief to see the robot could actually see and zero in on the ball&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Vape Cell EV - part IV - On battery charging</title><link>https://community.element14.com/thread/57192?ContentTypeID=0</link><pubDate>Sun, 16 Aug 2026 23:00:30 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:f1bc04f6-a735-4b9c-b80b-b7cadb600344</guid><dc:creator>saramic</dc:creator><slash:comments>3</slash:comments><comments>https://community.element14.com/thread/57192?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57192/vape-cell-ev---part-iv---on-battery-charging/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h1 id="recap"&gt;Recap&lt;/h1&gt;
&lt;p&gt;Can random Vape Cells with unknonw histories be bundled into a smart battery system to ultimately power an EV, power wall or other device?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57077/vape-cell-ev---part-i---what-s-in-a-vape" data-e14adj="t"&gt;Vape Cell EV - part I - What&amp;rsquo;s in a Vape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57148/vape-cell-ev---part-ii---rs485-comms" data-e14adj="t"&gt;Vape Cell EV - part II - RS485 comms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57186/vape-cell-ev---part-iii---ina219-power-monitor" data-e14adj="t"&gt;Vape Cell EV - part III - INA219 power monitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="challanges"&gt;Challenges&lt;/h2&gt;
&lt;p&gt;Although I have been bit more busy recently with trips away, even though it is winter here in Australia , the biggest challenge is not fully knowing everything about battery charging and trying to take small steps to identify what is safe to do. Which has lead to stalling some of the things I wanted to do in this project.&lt;/p&gt;
&lt;h2 id="what-i-learnt"&gt;What I learnt&lt;/h2&gt;
&lt;p&gt;It seems that for most&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Li-ion&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;cells like the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;18650&lt;/strong&gt;, the key to charging them is CC (Constant Current) and CV (Constant Voltage). The cell has a nominal voltage of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;3.6V ~ 3.7V&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and usually a maximum charge voltage of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;4.20V&lt;/code&gt;. Cells can be chargec at between 0.2C - 1.0C where C is Capacity. For a cell that claims 6000mAh this would mean 0.2C 1.2A, 0.5C 3A or 1.0C 6A. These are indicative values and READ THE DOCUMENTATION FOR YOUR CELL. Not to say the least that I was not about to push 6A through my cell on a desk &lt;span style="font-size:30px;"&gt;[emoticon:ad09a845b95e4cb28c5d9684a032037c]&lt;/span&gt;. Even less so for an old vape cell that was not labled.&lt;/p&gt;
&lt;p&gt;Also cells can become damaged if they go below a minimum voltage, usually around 3V.&lt;/p&gt;
&lt;p&gt;A battery management system, BMS, allows multiple cells to be charged. A balance connector allows for each individual cell to be connected and managed for the above thresholds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;over charge protection&lt;/li&gt;
&lt;li&gt;over discharge protection&lt;/li&gt;
&lt;li&gt;over current protection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From what I can see in builds like Chris Doel&amp;rsquo;s &amp;ldquo;I Powered My House Using 500 Disposable vapes&amp;rdquo;, although he adds a per battery fuse for short protection, there is actually no BMS, no ability to check the levels of any individual cell and from my understanding, he has manually charged the cells and put them into a battery configuration which he will never be able to re-charge safely - a one off YouTube video stunt?&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/dy-wFixuRVU" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt="I Powered My House Using 500 Disposable vapes - Chris Doel" src="http://i.ytimg.com/vi/dy-wFixuRVU/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Interestingly in the vape cells, the over current protection is built into the vape cell wire - if you short the battery, the wire will immediately burn through and cut off any more current flow.&lt;/p&gt;
&lt;p&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/vape_5F00_cell_5F00_wire_5F00_fuse.gif" /&gt;&lt;/p&gt;
&lt;p&gt;In above video you can see when I short out the cell (DO NOT TRY THIS AT HOME) the wire from the vape cell is so thin, it burns out and acts as a fuse to immediately stop current flow.&lt;/p&gt;
&lt;p&gt;One of the online BMS circuits&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://github.com/stuartpittaway/diyBMS" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/stuartpittaway/diyBMS&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;allows you to monitor each cell via a web interface powered by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;ESP8266&lt;/code&gt;. Interestingly over charge protection seems not to turn off the charging of the battery but redirecting the extra current into a big 3Ω3 10W resistor, which seems OK for balancing batteries that have been overcharged but seems like overkill to have that per battery, a resistor the size of a battery just to charge it? an informative overview can be seen in Great Scott&amp;rsquo;s DIY or Buy segment&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/rT-1gvkFj60" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt=" " src="http://i.ytimg.com/vi/rT-1gvkFj60/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;span&gt;Electronoobs also has a hand built BMS. This circuit uses a zener diode to redirect extra current into a load of 4 diodes, again I am not fully sure of this but it does feel wasteful. More can be seen in the video below&lt;/span&gt;&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/qRVEJjk5B_g" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt="Homemade BMS - Balanced LiPo Charger Multiple Cells and Current Limit - Electronoobs" src="http://i.ytimg.com/vi/qRVEJjk5B_g/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;span&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260819_5F00_bms_5F00_diode_5F00_load.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Finally Haase Industris seems to point out some of the limitations of the above circuits and proposes using a&amp;nbsp;&lt;a id="" href="https://www.ti.com/product/BQ77915" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;BQ77915&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/UUr-CJudg38" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt="Most BMSs (Battery Management Systems) don't cut it... I Built a BETTER One. - Haase Industries" src="http://i.ytimg.com/vi/UUr-CJudg38/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&lt;span&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260819_5F00_bms_5F00_chip_5F00_BQ77915.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In between this I saw a video showing how overcharging can explode a cell&lt;/p&gt;
&lt;p&gt;And Adam Savage&amp;rsquo;s overview of 18650 lithium-Ion batteries with a review from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://www.lumafield.com/battery-report" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Lumafield&amp;rsquo;s Battery quality report&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;which seems to sugges that anything other than a branded cell is a ticking time bomb. Their X-ray like technology showing build flaws in batteries&lt;/p&gt;
&lt;p&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/pastedimage1787104139596v1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;See the full video here:&lt;/span&gt;&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/-Y23nfAOiXQ" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt="The Surprising Flaws in 18650 Lithium-Ion Batteries - Adam Savage&amp;rsquo;s Tested" src="http://i.ytimg.com/vi/-Y23nfAOiXQ/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;That said, a recent YouTube short from Great Scott suggests it is actually quite hard to blow one of these batteries up&lt;/p&gt;
&lt;div style="margin:auto;max-width:240px;"&gt;&lt;a href="https://youtu.be/DQrXqrMx2z8" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt; &lt;img alt="This Unprotected Battery Surprised Me! - Great Scott" src="http://i.ytimg.com/vi/DQrXqrMx2z8/hqdefault.jpg" /&gt; &lt;/a&gt;&lt;/div&gt;
&lt;p&gt;as you can see, a lot of information and heat[emoticon:d3f592d49e434d7fb67e973881d4c367]&amp;nbsp;generated that took me a little bit out of my comfort zone.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img style="max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260819_5F00_battery_5F00_explosion.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;care of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://youtu.be/n3urBpFIBgY" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;How to keep LiPos from burning down your house (safe lipo charging) - Joshua Bardwell&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="up-next"&gt;Up Next&lt;/h2&gt;
&lt;p&gt;Using the INA219 curernt/voltage monitor from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57186/vape-cell-ev---part-iii---ina219-power-monitor" data-e14adj="t"&gt;Vape Cell EV - part III - INA219 power monitor&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;I plan to cycle a known 18560 cell and a vape cell to see how close my tracking is to what is written on the packaging&lt;/p&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a id="" href="https://github.com/saramic/vape-cell-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/saramic/vape-cell-EV&lt;/a&gt;&lt;/p&gt;</description></item><item><title>RE: Vape Cell EV - part IV - On battery charging</title><link>https://community.element14.com/thread/237926?ContentTypeID=1</link><pubDate>Sun, 23 Aug 2026 10:50:59 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:42c3ebbd-88d0-4c79-a3a4-a1bb41a085ce</guid><dc:creator>saramic</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237926?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57192/vape-cell-ev---part-iv---on-battery-charging/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;yeah there is a bunch of changes I have seen recently - all battery packs on a flight are meant to be within hands reach - of course they cannot be too powerful 100wh or around 27,000 mAh seems to be the limit, local trains no longer allow homemade/converted e-bikes and scooters and there are a bunch of building codes for home batteries&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Vape Cell EV - part IV - On battery charging</title><link>https://community.element14.com/thread/237924?ContentTypeID=1</link><pubDate>Sun, 23 Aug 2026 02:19:53 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:37930aed-c470-42cd-8a88-42c18f89b5e8</guid><dc:creator>kmikemoo</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/237924?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57192/vape-cell-ev---part-iv---on-battery-charging/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;There&amp;#39;s a reason that some municipalities are requiring battery storage systems be placed outside the main house.&amp;nbsp; While it&amp;#39;s highly unlikely that your battery storage system will catch fire, if it does... the consequences are pretty significant.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Challengers, Post your Projects!</title><link>https://community.element14.com/thread/237914?ContentTypeID=1</link><pubDate>Fri, 21 Aug 2026 14:58:13 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:2bc713a7-7ed7-4b9f-a923-4da511c1599b</guid><dc:creator>JoRatcliffe</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237914?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57178/challengers-post-your-projects/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Great project submissions and updates, everyone!&amp;nbsp;I will announce the winners on Monday, have a&amp;nbsp;good weekend &lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f642.svg" title="Slight smile"&gt;&amp;#x1f642;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Challengers, Post your Projects!</title><link>https://community.element14.com/thread/57178?ContentTypeID=0</link><pubDate>Fri, 14 Aug 2026 15:12:06 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:611482fc-ee00-45d8-a65c-da153efd9975</guid><dc:creator>cstanton</dc:creator><slash:comments>14</slash:comments><comments>https://community.element14.com/thread/57178?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57178/challengers-post-your-projects/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;All accepted challengers should now be able to post their final write-ups in:&amp;nbsp;&amp;nbsp;[mention:9b0ab29684b5482d90716e7898532942:ca0e7c8086864d2fa5a863b9e212e922]&amp;nbsp;&lt;/p&gt;
&lt;p&gt;We&amp;#39;ve had some team members out of the office, so this may be a little short notice (we usually give a week for write-ups) so if you&amp;#39;re a&amp;nbsp;&lt;em&gt;few&lt;/em&gt; days&lt;strong&gt; &lt;/strong&gt;late&amp;nbsp;I think&amp;nbsp;[mention:ef3eff32a0cb4cc7a5e89891f461c343:e9ed411860ed4f2ba0265705b8793d05]&amp;nbsp;will forgive you, but you have to let us know.&lt;/p&gt;
&lt;p&gt;Good luck&amp;nbsp;[emoticon:c4563cd7d5574777a71c318021cbbcc8]&lt;/p&gt;</description></item><item><title>DockBot - Part 6 - The Mostly Open Closed Loop Control</title><link>https://community.element14.com/thread/57203?ContentTypeID=0</link><pubDate>Tue, 18 Aug 2026 06:09:21 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:5ae5e618-f44e-4146-9dc4-e44e2bde42b6</guid><dc:creator>arvindsa</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57203?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57203/dockbot---part-6---the-mostly-open-closed-loop-control/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;I am building a robotic system that identifies the charging port on an EV and automatically moves a charger arm to plug the charger in.&lt;/p&gt;
&lt;p&gt;Past Forum Posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57056/dockbot---part-1---the-concept" data-e14adj="t"&gt;DockBot - Part 1 - The Concept&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57069/dockbot---part-2---positioning-with-aruco-markers" data-e14adj="t"&gt;DockBot - Part 2 - Positioning with Aruco Markers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/p/addpost/community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57142/dockbot---part-3---new-plan-new-hardware-for-better-sensing?COM=e14c-direct-ugc&amp;amp;CMP=e14c-direct-ugc&amp;amp;osetc=e14c-direct-ugc" target="_blank" data-e14adj="t"&gt;DockBot - Part 3 - New Plan, New Hardware for Better Sensing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57162/dockbot---part-4---getting-the-arduino-q-to-move-the-tank-motors" data-e14adj="t"&gt;DockBot - Part 4 - Getting the Arduino Q to move the tank motors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57187/dockbot---part-5---the-mechanical-hand" data-e14adj="t"&gt;DockBot - Part 5 - The Mechanical Hand&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wanted to have a proper closed loop control of my motors using an magenetic encoder TLE5012 which i have in surplus but I did not get the ordered diametrical magnets in time. So i had to do yet another work around. Use the Aruco marker as a sensor, move the robot incrementally, correct the motors. and repeat till the target. Sounds Simple. But Nooope.&lt;/p&gt;
&lt;h2 id="mcetoc_1k09k6tte0"&gt;Motor Calibration. Take 1&lt;/h2&gt;
&lt;p&gt;The idea was simple, run one motor at a known PWM speed for a small time, see how much the robot turns, through aruco marker, then incrementally increase and see the difference. Then the opposite direction. Left one worked out fine, but right one never turned on. It seems that while the left one started at PWM 84/255 duty, the right one started only at 184/255. This caused the right motor to never start in the calibration algorithm I made. Also, i noticed a fundamental flaw, the smaller bursts meant the tracks were slipping on my floor, and there was not enough time for it to stabilize the slipping. So this idea had to be junked&lt;/p&gt;
&lt;p&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/20260817_5F00_220222.mp4:640:360]&lt;/p&gt;
&lt;h2 id="mcetoc_1k09kr7171"&gt;Motor calibration, take 2&lt;/h2&gt;
&lt;p&gt;I decided to just junk auto calibration, and went the cannibal way. I decided to use constant motor speeds one set speed for each of forward, reverse, rotate left and rotate right. I kept tracking the motion and manually adjusted the speed&amp;nbsp;&lt;/p&gt;
&lt;p&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/p6_2D00_v1.mp4:640:360]&lt;/p&gt;
&lt;p&gt;If you noticed, there was a slight flutter in the marker due to the ceiling fan&amp;#39;s air current. This did create some issues. So i decided to make it more robust by creating a cube out of cardboard.&lt;/p&gt;
&lt;p&gt;&lt;img style="max-height:450px;max-width:800px;" alt=" " src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/456/20260817_5F00_230227.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The tank now wears a small cube with a marker on each face - back, top, left, right - instead of the single (actually two) rear marker from before. The point is that whichever face happens to be pointed at the camera, something is always trackable, even when it rotates, not just when the tank is driving directly away from it. Now, I never managed to get multi point tracking working, you will see that i had to resort to some hand adjustment of the robot, but with time I can get it fixed.&lt;/p&gt;
&lt;h2 id="mcetoc_1k09lrlhu2"&gt;Actually approaching a target: three wrong designs first&lt;/h2&gt;
&lt;p&gt;The Radxa&amp;#39;s camera is fixed - mounted separately. It is not mounted on the robot. I wrote something that would spin the tank until the target marker was centered in the camera frame, the way you&amp;#39;d steer if the camera were on the robot&amp;#39;s. Since the camera doesn&amp;#39;t move when the robot spins, and the target doesn&amp;#39;t move either, the target&amp;#39;s position in the frame&amp;nbsp; - never changes no matter what the robot does. The script just spun in place for forty steps, learning nothing. The fix: infer heading from how the robot&amp;#39;s own marker position shifts in the frame, sampled before and after a small forward nudge. That nudge does double duty - it&amp;#39;s both the heading measurement and real progress toward the goal, so nothing is wasted purely on sensing.&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:b8aa06b1-7c78-4be8-8c8d-3f63d3deaeb7:type=python&amp;text=heading%20%3D%20%28rx1%20-%20rx0%2C%20rz1%20-%20rz0%29%20%20%20%23%20where%20the%20robot%27s%20own%20marker%20moved%0Abearing%20%3D%20%28goal_x%20-%20rx1%2C%20goal_z%20-%20rz1%29%20%20%20%23%20where%20the%20goal%20is%20from%20here%0Aangle%20%3D%20signed_angle_deg%28%2Aheading%2C%20%2Abearing%29%20%2A%20turn_sign]&lt;/p&gt;
&lt;p&gt;Second wrong design: I started the approaching using raw pixel calulation. But I moved from raw pixel positions to real millimeters via &lt;code&gt;cv2.solvePnP&lt;/code&gt;, which has a sign convention of its own - &lt;code&gt;--turn-sign&lt;/code&gt; that worked for the pixel-only version was backwards for the mm version, and this caused the robot to move away from the target by making an u Turn.&lt;/p&gt;
&lt;p&gt;Third: close to the target&amp;#39;s marker isn&amp;#39;t the same as &amp;quot;in front of it.&amp;quot; solvePnP gives you the marker&amp;#39;s own facing direction, not just its position - I&amp;#39;d been computing distance to a point, not distance to a pose. Fixed by projecting a goal point along the target&amp;#39;s own facing normal, sampled once at startup since the target is static, rather than the marker&amp;#39;s raw position.&lt;/p&gt;
&lt;p&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/trial.mp4:800:450]&lt;/p&gt;
&lt;h2 id="mcetoc_1k09o1alf0"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Once these were fixed, the robot became tamed, There were many fine tuning steps in between but they are not very educative. Now to the final step of Getting the Charging Handle to a car and also before that Improve the accuracy.&lt;/p&gt;</description></item><item><title>RE: DockBot - Part 6 - The Mostly Open Closed Loop Control</title><link>https://community.element14.com/thread/237913?ContentTypeID=1</link><pubDate>Fri, 21 Aug 2026 00:54:14 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:30513de5-cdc5-4eb3-846d-2fbe0bcc7a71</guid><dc:creator>arvindsa</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237913?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57203/dockbot---part-6---the-mostly-open-closed-loop-control/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Thanks.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: DockBot - Part 5 - The Mechanical Hand</title><link>https://community.element14.com/thread/237912?ContentTypeID=1</link><pubDate>Fri, 21 Aug 2026 00:53:13 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:95382a22-2967-4a2d-a712-74c892bc39d5</guid><dc:creator>arvindsa</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237912?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57187/dockbot---part-5---the-mechanical-hand/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Thank you.&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>DockBot - Part 5 - The Mechanical Hand</title><link>https://community.element14.com/thread/57187?ContentTypeID=0</link><pubDate>Sun, 16 Aug 2026 17:55:55 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:20380f14-87f3-4cc9-912e-e73b1dc879e4</guid><dc:creator>arvindsa</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57187?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57187/dockbot---part-5---the-mechanical-hand/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jvl2tclo0"&gt;Recap&lt;/h2&gt;
&lt;p&gt;I am building a robotic system that identifies the charging port on an EV and automatically moves a charger arm to plug the charger in.&lt;/p&gt;
&lt;p&gt;Past Forum Posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57056/dockbot---part-1---the-concept" data-e14adj="t"&gt;DockBot - Part 1 - The Concept&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57069/dockbot---part-2---positioning-with-aruco-markers" data-e14adj="t"&gt;DockBot - Part 2 - Positioning with Aruco Markers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/p/addpost/community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57142/dockbot---part-3---new-plan-new-hardware-for-better-sensing?COM=e14c-direct-ugc&amp;amp;CMP=e14c-direct-ugc&amp;amp;osetc=e14c-direct-ugc" target="_blank" data-e14adj="t"&gt;DockBot - Part 3 - New Plan, New Hardware for Better Sensing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57162/dockbot---part-4---getting-the-arduino-q-to-move-the-tank-motors" data-e14adj="t"&gt;DockBot - Part 4 - Getting the Arduino Q to move the tank motors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is going to be a short post, the kind you write finishing your assignment on the eve of the deadline. We have a robot base, we have a guidance system - what&amp;#39;s left (mechanically) is an actuator to actually place a mock charger on the car.&amp;nbsp;&lt;/p&gt;
&lt;h2 id="mcetoc_1k05pdqq40"&gt;The actuator design&lt;/h2&gt;
&lt;p&gt;With the platform able to move itself around the floor, all I needed was an actuator that could lift and position the mock charger at different heights. That height adjustment matters because different car models have their charging port at different heights, and the mock charging dock on the car side won&amp;#39;t always be identical either.&lt;/p&gt;
&lt;p&gt;I found a design that did exactly this at &lt;a href="https://www.thingiverse.com/thing:5225652/files" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;thingiverse.com/thing:5225652&lt;/a&gt; - credit for that goes to SnakeP (&lt;a href="https://www.thingiverse.com/SnakeP/designs" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;thingiverse.com/SnakeP&lt;/a&gt;). It worked brilliantly, and mounting it on the Devastator platform was painless.&lt;/p&gt;
&lt;p&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;" alt=" " src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/456/pastedimage1786900862605v1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I was able to lift around 300gms safely. I dare not try more, as I printed in the parts in the default infill of 15% and I do not have the time to reprint it if I broke it just to see how much weight it can lift.&lt;/p&gt;
&lt;p&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt=" " src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260813_5F00_144929.gif" /&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;h2 id="mcetoc_1k05q8sat5"&gt;The mock charger design&lt;/h2&gt;
&lt;p&gt;I wanted the charger itself to be forgiving of small positioning errors - remember Part 3 measured up to 3% error on positioning at range. So I leaned into a &lt;a href="https://en.wikipedia.org/wiki/Poka-yoke" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;poka-yoke design&lt;/a&gt;&amp;nbsp;(Mistake Proofing) : the charger is made from a length of PVC pipe with a flange on each end. Eventually I&amp;#39;ll add a magnet so it sticks to the car once docked. For the robot to actually grab it, I gave the handle two chamfered collars, so the geometry itself nudges the handle into alignment even if the robot&amp;#39;s approach isn&amp;#39;t perfectly centered.&lt;/p&gt;
&lt;p&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;" alt=" " src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/456/20260816_5F00_225853_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1k05q8sat6"&gt;The grabber design&lt;/h2&gt;
&lt;p&gt;The grabber is four fingers with a wide opening - essentially a V shape that guides the handle into the locking area, oriented perpendicular to the charging handle itself. Between the chamfered collar and the V fingers, the mechanism forgives about &amp;plusmn;1.8cm of positioning error.&lt;/p&gt;
&lt;p&gt;I also designed a small dock for the charger to rest in when it&amp;#39;s not &amp;quot;charging a car.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;" alt=" " src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/456/20260816_5F00_225905_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;[View:/cfs-file/__key/communityserver-discussions-components-files/456/p5_2D00_v1.mp4:800:450]&lt;/p&gt;
&lt;h2 id="mcetoc_1k05q8sat8"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;The results worked brilliantly in my hand. Now I hope things don&amp;#39;t fall apart tomorrow when i integrate everything. I still have not received the diametrically magnetized magnet for my encoder. So I will have to improvise (again???, This entire projects had way too many improvisations)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description></item><item><title>RE: hall-w-EV - Post 4 - Giving it eyes</title><link>https://community.element14.com/thread/237911?ContentTypeID=1</link><pubDate>Thu, 20 Aug 2026 19:34:31 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:3cb3fcf3-24f8-4cdd-b936-af84eb3ebb14</guid><dc:creator>DAB</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/237911?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57184/hall-w-ev---post-4---giving-it-eyes/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Nice update.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: hall-w-EV - Post 5 - Chase ball</title><link>https://community.element14.com/thread/237910?ContentTypeID=1</link><pubDate>Thu, 20 Aug 2026 19:32:11 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:213f78e1-aec5-4dd3-b722-ca0b835e07fd</guid><dc:creator>DAB</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/237910?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57185/hall-w-ev---post-5---chase-ball/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Nice update.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Vape Cell EV - part III - INA219 power monitor</title><link>https://community.element14.com/thread/237909?ContentTypeID=1</link><pubDate>Thu, 20 Aug 2026 19:31:14 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:d935cbc7-c860-4d9d-b4f1-e79b07263b2f</guid><dc:creator>DAB</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/237909?ContentTypeID=1</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57186/vape-cell-ev---part-iii---ina219-power-monitor/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Nice update.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Vape Cell EV - part III - INA219 power monitor</title><link>https://community.element14.com/thread/57186?ContentTypeID=0</link><pubDate>Sun, 16 Aug 2026 15:06:25 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:3fae09d9-a4c5-4b2b-a390-ae74673109a5</guid><dc:creator>saramic</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/57186?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57186/vape-cell-ev---part-iii---ina219-power-monitor/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Our consumerist society throws out millions of rechargeable vape cells, but the more I read about it sounds like lithium batteries should explode all the time.&lt;/p&gt;
&lt;h1 id="recap"&gt;Recap&lt;/h1&gt;
&lt;p&gt;Vape Cell EV is the idea of a smart cell charging system that can handle cells with unknown histories, charging, power distribution, any safety issues and ultimately if a cell should be removed from a battery of many cells.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57077/vape-cell-ev---part-i---what-s-in-a-vape" data-e14adj="t"&gt;Vape Cell EV - part I - What&amp;rsquo;s in a Vape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/ez-ev-challenge/f/forum/57148/vape-cell-ev---part-ii---rs485-comms" data-e14adj="t"&gt;Vape Cell EV - part II - RS485 comms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ina219"&gt;INA219&lt;/h2&gt;
&lt;p&gt;To characterise a cell you need at least three things: voltage, current, and time. Voltage alone tells you roughly where you are on the discharge curve. Current alone tells you the load. Put them together over time and you get energy in milliwatt-hours &amp;mdash; which is the actual capacity number you are trying to measure. The INA219 gives you all three from a single cheap I2C chip.&lt;/p&gt;
&lt;p&gt;It works by sitting in series with the circuit. A small shunt resistor &amp;mdash; 0.1 &amp;Omega; on the standard breakout module &amp;mdash; is placed between VIN+ and VIN&amp;minus;. The INA219 measures the tiny voltage drop across that shunt (10 mV per amp at 0.1 &amp;Omega;) and from that calculates current. Simultaneously it measures the bus voltage: the voltage at VIN&amp;minus; relative to GND, which is the voltage the load actually sees. The series resistance is small enough that it barely affects the circuit &amp;mdash; 0.1 &amp;Omega; in series with a 10 &amp;Omega; load is less than 1% overhead.&lt;/p&gt;
&lt;p&gt;The first real test was a known 18650 cell discharged through a 10 &amp;Omega; / 5 W resistor: the INA219 read&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;~385 mA, ~3.87 V, ~1420 mW&lt;/strong&gt;. The resistor got warm, as expected &amp;mdash; 1.4 W in a 5 W part is entirely within rating. The session tracking in the dashboard accumulates mWh in real time, which means leaving it running through a full discharge gives you the cell capacity directly, without any manual timing or maths.&lt;/p&gt;
&lt;h2 id="wirebegin-isnt-optional"&gt;Wire.begin() isn&amp;rsquo;t optional&lt;/h2&gt;
&lt;p&gt;On this platform,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Wire2.begin()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;isn&amp;rsquo;t just good practice &amp;mdash; without it, every I2C call fails immediately regardless of wiring. The Zephyr arduino port uses lazy initialisation: the hardware peripheral doesn&amp;rsquo;t start until you explicitly call&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;begin()&lt;/code&gt;. Nothing about the error messages hints at this; everything just returns 1.&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:7337141d-2622-4e78-abc2-ef7cd293b9fb:type=c_cpp&amp;text=void%20setup%28%29%20%7B%0A%20%20%20%20Bridge.begin%28%29%3B%0A%20%20%20%20Wire2.begin%28%29%3B%20%20%20%2F%2F%20i2c3%3A%20A4%3DSDA%2C%20A5%3DSCL%0A%20%20%20%20%2F%2F%20...%0A%7D]&lt;/p&gt;
&lt;h2 id="current-without-voltage"&gt;Current without voltage&lt;/h2&gt;
&lt;p&gt;First real test with an 18650 across a 10 &amp;Omega; resistor: current read correctly at about 196 mA, but bus voltage was 0.012 V &amp;mdash; effectively zero. The fix was obvious once the INA219&amp;rsquo;s measurement model clicked: bus voltage is VIN&amp;minus;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;relative to GND&lt;/em&gt;, so if the battery&amp;rsquo;s negative terminal isn&amp;rsquo;t connected to the INA219&amp;rsquo;s GND pin, the reference floats. Current measurement is differential (VIN+ minus VIN&amp;minus;) and works regardless &amp;mdash; voltage measurement doesn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Once a wire ran from the battery negative node to GND: 3.87 V, 385 mA, 1420 mW.&lt;/p&gt;
&lt;h2 id="a-silent-json-truncation"&gt;A silent JSON truncation&lt;/h2&gt;
&lt;p&gt;With everything reading correctly on the MCU, the web dashboard was stuck on NO LINK. The Python side was receiving data but failing to parse it:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:6df77249-3235-476d-957a-89e0d886a5d6:type=text&amp;text=%5Bbridge%2Fconfig%5D%20Expecting%20%27%2C%27%20delimiter%3A%20line%201%20column%2029%20%28char%2028%29]&lt;/p&gt;
&lt;p&gt;Tracing back through the JSON the sketch was producing, the config handler built the sensor address string into a fixed buffer:&lt;/p&gt;
&lt;p&gt;[embed:dc8ab71f-3b98-42d9-b0f6-e21e02a0f8e2:e7958989-4306-4b0d-915c-bf6e70ace7aa:type=c_cpp&amp;text=char%20addrStr%5B6%5D%3B%0Asnprintf%28addrStr%2C%20sizeof%28addrStr%29%2C%20%22%5C%220x%2502X%5C%22%22%2C%20sensors%5Bi%5D.addr%29%3B]&lt;/p&gt;
&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;&amp;quot;0x40&amp;quot;&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is six characters plus a null terminator &amp;mdash; seven bytes. The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;snprintf&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;quietly truncated the closing&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;&amp;quot;&lt;/code&gt;, leaving the JSON&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;["0x40,"rShunt":...]&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; which is a parse error exactly at character 28. Bumping the buffer to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;char addrStr[8]&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;fixed it.&lt;/p&gt;
&lt;h2 id="the-display"&gt;The display&lt;/h2&gt;
&lt;p&gt;With readings flowing, there&amp;rsquo;s a live dashboard served directly from the UNO Q&amp;rsquo;s MPU:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Per-sensor cards showing bus voltage (amber), current (green), power&lt;/li&gt;
&lt;li&gt;Dual-axis sparklines with fixed scales &amp;mdash; 0&amp;ndash;6 V and 0&amp;ndash;500 mA &amp;mdash; so a steady reading looks like a flat line instead of thrashing wall to wall&lt;/li&gt;
&lt;li&gt;Live CSV logging and a download button&lt;/li&gt;
&lt;li&gt;Session tracking: name a trace, press START, watch mWh accumulate in real time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img style="max-height:360px;max-width:640px;" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/456/20260816_5F00_INA219_5F00_discharge_5F00_dashboard.png" alt=" " /&gt;&lt;/p&gt;
&lt;h2 id="next"&gt;Next&lt;/h2&gt;
&lt;p&gt;Actual cell characterisation &amp;mdash; charge a known 18650 to 4.2 V, discharge it through a measured load down to 3.0 V, and get a real capacity number. Then do the same with salvaged vape cells and see how they compare.&lt;/p&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a id="" href="https://github.com/saramic/vape-cell-EV" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/saramic/vape-cell-EV&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>