<?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/on-the-line/f/forum</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><lastBuildDate>Sun, 21 Jun 2026 22:33:34 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum" /><item><title>Misaz’s Forum #5: Experimenting with Thermocouple</title><link>https://community.element14.com/thread/57055?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 22:33:34 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:36dcf1eb-2dea-4d7d-84c0-e19f1ca6cd39</guid><dc:creator>misaz</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57055?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57055/misaz-s-forum-5-experimenting-with-thermocouple/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;Hi. I welcome you to next part of my series about my experiments with industrial sensors. In this part I will look at thermocouple. As part of kit, I&amp;rsquo;ve got kit of five K-Type thermocouple. Compared to previously described sensor, thermocouple is complete opposite. It do not produce current output, instead it produces voltage output, and instead of getting decent industrial reliable output, it actually generates only few millivolts. While previous sensor was providing absolute value, thermocouples provide relative output to some reference called cold junction. It is temperature on connector where different metal touch each other. Sensor provides temperature difference between probe and this connector. To get absolute measurement, we need to measure one of these temperatures by different sensor. But Let&amp;rsquo;s begin simply. Let&amp;rsquo;s start with multimeter again.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;As a first experiment I connected multimeter in mili-voltmeter mode directly to sensor:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/19_2D00_ktype_2D00_cold.jpeg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And when I heated a probe a little I&amp;rsquo;ve got slightly higher voltage:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20_2D00_ktype_2D00_hot.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Note that measurements are not in V. They are in mV. So, it is 0,00023V actually. Very low voltage!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I tried Fluke online calculator at: &lt;a href="https://www.fluke.com/en-us/learn/tools-calculators/thermocouple-voltage-to-temperature-calculator" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;www.fluke.com/.../thermocouple-voltage-to-temperature-calculator&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/21_2D00_ktype_2D00_calc.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;As you can see, such small voltage difference correspond to 5,810 &amp;deg;C temperature difference between cold junction and thermocouple. It corresponds to difference between room temperature about 29&amp;deg;C these days and my finger temperature around 36&amp;deg;C.&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm4utgh0"&gt;&lt;span&gt;Let&amp;rsquo;s try digitally&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;For using thermocouples in real, we need to deal with two problems. Very low voltage and need for second sensor. Back in 2022, there were &lt;a href="https://community.element14.com/challenges-projects/design-challenges/experimenting-with-thermistors/" data-e14adj="t"&gt;Experimenting with Thermistors&lt;/a&gt; competition which I joined. Thermistors are one of the possible solutions for second sensor need. Additionally, as part of Experimenting with Thermistor I created board with 24-bit ADC with PGA which can nicely measure both thermistor and thermocouple. It is built around MAX11410 ADC chip. I reused it for this project. The idea was to reuse current part for thermistor and reference:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/22_2D00_ktype_2D00_dig_2D00_sch1.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Chip has build int constant current generator, which can generate current like 50uA and pass it from port like AIN4 here. This current flow down through thermistor and 22k precise (0,1% tolerance resistor) which generates reference (REF1). This can nicely measure thermistor relatively to known reference without any precise voltage reference (50uA current is also imprecise).&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For thermocouple I had plan to use another two channels of my board like Maxim showcase in ADC chip datasheet (however I used AIN7 and 8 since 4, 5, 6 are already occupied):&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/23_2D00_ktype_2D00_dig_2D00_sch2.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For this measurement, I actually need precise voltage reference. My idea was to use builtin reference, measure precise resistor using it, estimate it&amp;rsquo;s error and compensate thermocouple voltage measurement with this knowledge.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I connected everything:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/24_2D00_ktype_2D00_dig_2D00_real.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Note that thermistor end (two thin black wires ending on yellow connector) touches cold junction of thermocouple.&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm4utgh1"&gt;&lt;span&gt;Failed&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;However, my experiment failed. I migrated my MAX11410 library to Arduino Uno Q, adjusted code to do measurements as needed, but if for some reason returned full scale voltage in nearly all cases. As part of troubleshooting, I tried run the same experiment which I run back in Experimenting with Thermistors era, on same board, with same code, with disconnected other new parts and it still produce bad numbers.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I am not sure what happened to my ADC board, but I am, no longer do even basic voltage measurements with it. After few hours of troubleshooting, I gave up.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And since time is running out. This is end of my experimenting here. In next part, I will summarize all my efforts.&lt;br /&gt; &lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Misaz’s Forum #4: Digital measurement of Analog output</title><link>https://community.element14.com/thread/57054?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 22:30:50 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:2e4495e9-2578-4b78-ad2b-38a8d034acfc</guid><dc:creator>misaz</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57054?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57054/misaz-s-forum-4-digital-measurement-of-analog-output/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;Hello Element14 community.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In this forum I will show another play with Omega&amp;rsquo;s HX94C industrial humidity and temperature sensor with analog output. Today, I will convert analog current output to digital form using Arduino Uno Q.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;How to do that is briefly explained in printed manual which come with sensor:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/7571.15_2D00_manual_2D00_adc.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The missing part is &amp;ldquo;TEMP METER OR RECORDER&amp;rdquo;. Diagram highlights that it needs some resistor to measure. This is exactly what we need for converting current output to voltage measurable by ADC. Question is what value? The answer is ohm law. We need to map 20mA to 3.3V measured by Arduino UNO Q. Formula is: R = U / I. U is voltage we want to get (3.3V) at maximum current (0.020 A). 3.3 / 0.020 = 165 which means that we need about 165 ohm resistor. I do not have 165 ohm resistor, however I have lot of 330 ohm, so I will use two of them in parallel which result in exactly 165 ohm resistance. Connection is as follows:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/1033.16_2D00_exp2_2D00_sch.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In real life:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/7462.17_2D00_exp2_2D00_real.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Now let&amp;rsquo;s write sketch. Arduino Uno Q support more or less same sketches as older Arduino Unos. Difference is only in using Monitor instead of Serial because it needs utilize interconnection between chips. You can create new app in App Lab by clicking or in terminal:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;arduino-app-cli app new analog_humi_temp
cd /home/arduino/ArduinoApps/analog_humi_temp&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Then you can edit sources using your favourite editor. Sketch is in sketch/sketch.ino:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;vim sketch/sketch.ino&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Sketch source code:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void setup() {
  Monitor.begin(9600);
}

void loop() {
  int adcTemp = analogRead(A0);
  float voltageTemp = 3.3 * adcTemp / 1024.0;
  float currentTemp = 1000 * voltageTemp / 165.0;
  float temperature = (currentTemp - 4) * 100 / 16;

  int adcHumi = analogRead(A1);
  float voltageHumi = 3.3 * adcHumi / 1024.0;
  float currentHumi = 1000 * voltageHumi / 165.0;
  float humidity = (currentHumi - 4) / 0.16;

  Monitor.print(&amp;quot;Temperature: &amp;quot;);
  Monitor.print(temperature);
  Monitor.print(&amp;quot;\tHumidity: &amp;quot;);
  Monitor.println(humidity);
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And then you can run it by clicking Run in App lab, or in command line:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;arduino-app-cli app start .&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And logs you can observe using following command:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;arduino-app-cli monitor&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;It should look as follows:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/5327.18_2D00_exp2_2D00_results.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it. Outputs from industrial analog sensor are now in hands of modern fully digital Arduino Uno Q. In next part I will look to other sensor kit: Omega&amp;rsquo;s K-Type Thermocouple.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Misaz’s Forum #3: Analog measurements without any digital part</title><link>https://community.element14.com/thread/57053?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 22:16:19 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:237238d3-dba0-42dc-94d2-0adabcae29b8</guid><dc:creator>misaz</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57053?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57053/misaz-s-forum-3-analog-measurements-without-any-digital-part/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;Hi. I welcome you to my third forum post as part of my participation in &lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/" data-e14adj="t"&gt;On The Line Design Challenge&lt;/a&gt;. In this post, I will show how to measure temperature and humidity using it with using just sensor, power supply, and two multimeters (one for temperature, second for humidity).&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm41k6n0"&gt;&lt;span&gt;Current Outputs&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;The variant of sensor which we received as part of competition has current output. It works as current limiter. Sensor has no ground connection. It connects just to high voltage of power supply which need to be higher then 6V and maximum is listed as 30V. The sensor consume energy from this source and limit&amp;rsquo;s own power consumption based on measured properties. Output is in the range 4 &amp;ndash; 20mA, so sensor always consume at least 4mA which it uses for its own operation. Remaining current is &amp;ldquo;wasted&amp;rdquo; for indicating measured value.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Let&amp;rsquo;s measure some currents which corresponds to humidity and temperature measured by the sensor. Schematics of connection is following. As I mentioned, power pins of sensor (1 and 2) goes to positive voltage of power supply, output of sensors (3 and 4) are connected via amperemeters to negative pin of power supply.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/12_2D00_exp_2D00_sch.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In reality when powered:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/13_2D00_exp_2D00_real.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm41k6n1"&gt;&lt;span&gt;Converting Milliamperes to Temperature and Humidity&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Formulas for converting measured current (mA) to real values are mentioned in printed manual which come with sensor:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/14_2D00_manual_2D00_formulas.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I measured 7.77 mA on temperature output channel, and 13.020 mA on humidity channel. Let&amp;rsquo;s pass it to formulas:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;deg;C = (7,77 &amp;ndash; 4) * (100 / 16) = 23.5625 &amp;deg;C&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;%RH = (13,020 &amp;ndash; 4) / 0,16 = 56.375 %&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Misaz’s Forum #2: Breadboarding Industrial Temp + Humidity Sensor</title><link>https://community.element14.com/thread/57052?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 22:14:41 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:d9e05f0b-b0b0-4c05-9f97-635d9be52a3f</guid><dc:creator>misaz</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/57052?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57052/misaz-s-forum-2-breadboarding-industrial-temp-humidity-sensor/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;Hi. I welcome you to my second forum post as part of my participation in &lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/" data-e14adj="t"&gt;On The Line Design Challenge&lt;/a&gt;. In this post, I will describe how I prepared Omega HX94 humidity + temperature sensor to enable experimenting with it on breadboard.&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm3tska0"&gt;&lt;span&gt;Preparing Cable&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;First step is preparing cable for connecting sensor. It could be definitely done in hacky way, but I tried less hacky way and actually used connector which was bundled in package and mounted it to ethernet cable. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Process is explained in printed manual which come with the sensor:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/06_2D00_manual_2D00_mnt.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;First step is separating connector by unscrewing tiny screw on side:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/07_2D00_conn_2D00_disasm.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Then you can solder, wires to four contacts inside connector.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/08_2D00_conn_2D00_sold.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;As you can see, I did not forget about heatshrink, however since contact are large, I need to heat them a lot and heartstring sooner then I needed.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Next step is pulling other part of connector from other side of cable:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/09_2D00_conn_2D00_asm.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Then you can push parts together and fit back small screw.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Another end I made breadboard friendly:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/10_2D00_con_2D00_breadboard.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Across whole process, it is good idea to check for open connections and short circuits, but I was lucky this time. This is a result. Now my sensor is breadboarded:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/11_2D00_conn_2D00_final.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It is sensor with analog output, so next time, I will try it with power supply and multimeter. These two are enough to obtain reading from it.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Misaz’s Forum #1: Kit Unboxing</title><link>https://community.element14.com/thread/57051?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 22:12:40 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:626afe3d-b740-4b86-af26-adc439ad218f</guid><dc:creator>misaz</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57051?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57051/misaz-s-forum-1-kit-unboxing/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;Hello Element14 community. Welcome you to my first forum post about kit which I received as part &lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/" data-e14adj="t"&gt;On The Line Design Challenge&lt;/a&gt;. I started experimenting pretty late this time and writing I started even later, so this time, I post everything on last day :)&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm3qkj60"&gt;&lt;span&gt;Motivation&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;My motivation was mainly to play with industrial sensors. Most probably everybody can measure temperature with DHT22 nowadays, but over the time I learnt that in electronics, things are not black and white, but rather different technologies have some pros and cons. So as part of challenge, I want to discover offered high quality industrial humidity sensor with analog output, thermocouples and CAN bus.&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrm3qkj61"&gt;&lt;span&gt;Unboxing&lt;/span&gt;&lt;/h1&gt;
&lt;p&gt;&lt;span&gt;Kit come at time. Contained everything I expected + Element14 swag (stickers, letter). The most expensive and interesting part is this analog temperature + humidity sensor from Omega. It comes in large box which contained mounting accessors, cable connector, wall mount accessories and PRINTED MANUAL. Really cool.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/01_2D00_humi_2D00_temp_2D00_sens.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Second most interesting part of kit for me is pack of 5 K-type thermocouples:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/02_2D00_thermocouples.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Another part is CAN PHY Shield. It is powered by chip from Maxim Integrated, so it is shield and evaluation kit in one:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/03_2D00_can_2D00_shield.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Finally there is some compute. It is not that interesting to me, because I already have and play with the same one. You can read my initial impressions from my first Uno Q &lt;a href="https://community.element14.com/challenges-projects/design-challenges/light-up-your-life/f/forum/56589/finisher-prize-for-light-up-your-life-design-challenge" data-e14adj="t"&gt;here&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/04_2D00_uno_2D00_q.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Finally, Molex pre-crimped cables as a goodie in the kit:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/05_2D00_molex_2D00_cabs.jpg" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In next forum post, I will prepare temperature and humidity sensor for operation.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Green Brain - Part V - LabView visualisation</title><link>https://community.element14.com/thread/57050?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 16:50:40 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:0f9b736b-4e38-4a8b-874b-a304c0f4ab46</guid><dc:creator>saramic</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57050?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57050/green-brain---part-v---labview-visualisation/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;had never heard of NI LabVIEW before this challenge but after watching some overviews and tutorials it is clearly an industry standard, so I thought I would give it a go.&lt;/p&gt;
&lt;h1 id="recap"&gt;Recap&lt;/h1&gt;
&lt;p&gt;Green Brain&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f33f.svg" title="Herb"&gt;&amp;#x1f33f;&lt;/span&gt; is the idea of tracking plant  nodes via an industrial&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;CAN bus&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/56921/green-brain---part-i---can-bus-introduction" data-e14adj="t"&gt;Green Brain - Part I - CAN bus introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57001/green-brain---part-ii---dev-setup" data-e14adj="t"&gt;Green Brain - Part II - Dev setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Green Brain - Part III - Water Cannon Chaos&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57049/green-brain---part-iv---nodes-well-that-ends-well" data-e14adj="t"&gt;Green Brain - Part IV - Nodes Well That Ends Well&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="connecting-to-labview"&gt;Connecting to LabVIEW&lt;/h2&gt;
&lt;p&gt;The UNO Q already runs a Python Flask REST API that serves the live sensor readings as JSON at&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;pollyanna.local:8080/.../nodes&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="json"&gt;[{&amp;quot;node&amp;quot;:2,&amp;quot;temp&amp;quot;:24.5,&amp;quot;hum&amp;quot;:61.0,&amp;quot;seq&amp;quot;:42,&amp;quot;ok&amp;quot;:true,&amp;quot;age&amp;quot;:3},
 {&amp;quot;node&amp;quot;:3,&amp;quot;temp&amp;quot;:23.1,&amp;quot;hum&amp;quot;:58.0,&amp;quot;seq&amp;quot;:38,&amp;quot;ok&amp;quot;:true,&amp;quot;age&amp;quot;:1}]&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;That means LabVIEW just needs to poll an HTTP endpoint &amp;mdash; no new code on the hardware side at all.&lt;/p&gt;
&lt;h2 id="what-i-tried--http-get-with-json-parsing"&gt;What I tried &amp;mdash; HTTP GET with JSON parsing&lt;/h2&gt;
&lt;p&gt;LabVIEW has a built-in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;HTTP Client GET&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;VI under&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Data Communication &amp;rarr; Protocols &amp;rarr; HTTP Client&lt;/code&gt;. Getting the raw JSON string back from the API worked fine.&lt;/p&gt;
&lt;p&gt;The stumbling block was parsing it. LabVIEW&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;Unflatten From JSON&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;needs a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;type&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;input &amp;mdash; an array-of-clusters constant whose structure exactly matches the JSON. You build it outside-in: place an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Array Constant&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;shell first, then drop a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Cluster Constant&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;inside it, then add typed constants for each field with labels matching the JSON keys exactly.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_lab_5F00_view_5F00_temp_5F00_and_5F00_humidity.png"  /&gt;&lt;/p&gt;
&lt;p&gt;I got as far as the HTTP response but hit a type mismatch error:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;LabVIEW: (Hex 0xFFFA4723) Type mismatch between JSON and LabVIEW.&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The debug approach suggested on the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href="https://forums.ni.com/t5/G-Web-Development-Software/json-parsing-array-of-cluster/td-p/4188063" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;NI forums&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is to wire the cluster-array constant to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Flatten To JSON&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;first and compare its output to the real API response &amp;mdash; whatever differs is the mismatch. I ran out of time to track mine down fully, but it is close.&lt;/p&gt;
&lt;h2 id="other-ways-to-connect-sensors-to-labview"&gt;Other ways to connect sensors to LabVIEW&lt;/h2&gt;
&lt;p&gt;For reference, here is how the options compare &amp;mdash; from simplest to most capable:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;th&gt;Good for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VISA Serial&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Arduino &amp;rarr; USB &amp;rarr; LabVIEW VISA Read VI&lt;/td&gt;
&lt;td&gt;Quick bench work, no network needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TCP stream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UNO Q opens a socket, streams CSV&lt;/td&gt;
&lt;td&gt;Low latency, one-to-one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP REST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LabVIEW polls a URL (what I used)&lt;/td&gt;
&lt;td&gt;Already have a server running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MQTT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UNO Q publishes, LabVIEW subscribes&lt;/td&gt;
&lt;td&gt;Multiple consumers, buffered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NI DAQmx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NI hardware plugs straight in&lt;/td&gt;
&lt;td&gt;Precision timing, industrial use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For temperature monitoring the timing requirements are relaxed, so all of these work in practice. HTTP polling was the natural fit here since the REST API already existed.&lt;/p&gt;
&lt;h2 id="next"&gt;Next&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/b/projects/posts/green-brain---distributed-greenhouse-control-platform" data-e14adj="t"&gt;Green Brain - distributed greenhouse control platform&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a id="" href="https://github.com/saramic/green-brain" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/saramic/green-brain&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Green Brain - Part IV - Nodes Well That Ends Well</title><link>https://community.element14.com/thread/57049?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 14:40:22 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:64f9f1cb-e7c4-41e0-b008-e1c8cebb8110</guid><dc:creator>saramic</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/57049?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57049/green-brain---part-iv---nodes-well-that-ends-well/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;I had built up some proto board nodes for the distributed greenhouse control platform &amp;mdash; it was time to wire them all up together and get them talking. It did not go smoothly&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f605.svg" title="Sweat smile"&gt;&amp;#x1f605;&lt;/span&gt;.&lt;/p&gt;
&lt;h1 id="recap"&gt;Recap&lt;/h1&gt;
&lt;p&gt;Green Brain&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f33f.svg" title="Herb"&gt;&amp;#x1f33f;&lt;/span&gt; is the idea of tracking plant  nodes via an industrial&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;CAN bus&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to ultimately see which plant needs&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;del&gt;more water cannon&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f52b.svg" title="Gun"&gt;&amp;#x1f52b;&lt;/span&gt;.&lt;/del&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;well watering.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/56921/green-brain---part-i---can-bus-introduction" data-e14adj="t"&gt;Green Brain - Part I - CAN bus introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57001/green-brain---part-ii---dev-setup" data-e14adj="t"&gt;Green Brain - Part II - Dev setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Green Brain - Part III - Water Cannon Chaos&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="three-nodes-one-bus"&gt;Three nodes, one bus&lt;/h2&gt;
&lt;p&gt;So far I had tested two nodes talking to each other on a work bench. Adding a bunch of twisted pair wiring and a third node to form a proper little network seemed straightforward &amp;mdash; it was far from it.&lt;/p&gt;
&lt;p&gt;The symptoms were baffling. The first two nodes would work, then one of them would drop off and I would have only one node, and sometimes no nodes would appear at all after some time. This went on in circles for a while, not to mention some circuit board issues early on that prevented two boards working altogether.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_all_5F00_nodes_5F00_in_5F00_greenhouse_5F00_and_5F00_a_5F00_plang.jpg"  /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;All the nodes set up in a greenhouse. You can see they are all connected with twisted pair. Clearly plywood is not the best for a humid greenhouse but I have a future use for the stands&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="problem-1--bad-wiring-and-cold-solder-joints"&gt;Problem 1 &amp;mdash; bad wiring and cold solder joints&lt;/h2&gt;
&lt;p&gt;The first and most humbling issue: bad physical connections.&lt;/p&gt;
&lt;p&gt;Node 1 would print this at startup and hang:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;ERR: setBitrate failed — check SPI wiring&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I had checked the wiring visually &amp;mdash; it looked fine. But to get a more precise diagnosis I added a register read&amp;nbsp;&lt;/span&gt;&lt;em&gt;before&lt;/em&gt;&lt;span&gt;&amp;nbsp;calling&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;setBitrate&lt;/code&gt;&lt;span&gt;, directly checking the&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;CANSTAT&lt;/code&gt;&lt;span&gt;&amp;nbsp;register on the MCP2515 chip over SPI:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;Serial.print(F(&amp;quot;CANSTAT after reset=0x&amp;quot;)); Serial.println(readCanstat(), HEX);
// expect 0x80 (config mode) — 0x00/0xFF means SPI not working
if (mcp2515.setBitrate(CAN_500KBPS, CAN_CLOCK) != MCP2515::ERROR_OK) {
    Serial.println(F(&amp;quot;ERR: setBitrate failed — check SPI wiring&amp;quot;));
    while (true);
}
&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Where&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;readCanstat()&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;does a raw SPI read of register&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x0E&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;uint8_t readCanstat() {
    SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
    digitalWrite(MCP_CS_PIN, LOW);
    SPI.transfer(0x03); // READ instruction
    SPI.transfer(0x0E); // CANSTAT register address
    uint8_t val = SPI.transfer(0x00);
    digitalWrite(MCP_CS_PIN, HIGH);
    SPI.endTransaction();
    return val;
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;What came back:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;CANSTAT after reset=0x0&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The three possible values tell you exactly what is broken:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CANSTAT&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chip alive, in config mode &amp;mdash; SPI working&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/2705.svg" title="White check mark"&gt;&amp;#x2705;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0xFF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MISO floating high &amp;mdash; chip not connected or MISO wire missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MISO pulled low &amp;mdash; CS not going low, or SCK missing, or a short to GND&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Getting&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x00&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;meant the chip was never being selected. After going back with a multimeter and checking continuity on every pin, I found a bad joint on the CS (D10) header pin &amp;mdash; it looked soldered but wasn&amp;rsquo;t making contact. A quick reflow fixed that joint, but&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x00&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;persisted. I tried loopback mode (which needs no CAN bus wiring at all &amp;mdash; the chip loops internally) and still got the same result. That ruled out everything external. The MCP2515 chip itself, or its crystal, was dead. As I didn&amp;rsquo;t have a spare module to swap out, I swapped with another node, to confirm the module was working, which it was - so there is an issue in some of my wiring on that node - will fix that before I start watering any plants with this system, for sure .&lt;/p&gt;
&lt;p&gt;The lesson:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;when SPI doesn&amp;rsquo;t work, measure before you guess, and work inward&lt;/strong&gt;. The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;CANSTAT&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;read costs two lines of code and points at which layer is broken &amp;mdash; wiring, connection, or chip &amp;mdash; without having to guess.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_node_5F00_in_5F00_greenhouse.jpg"  /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The node is a simple build centered around an Arduino Nano and connected to a number of sensors and the CAN transceiver. I have a &amp;ldquo;USB C&amp;rdquo; charger/power driver &amp;mdash; unfortunately I went cheap from China and they are not true USB C but just a cheap power cable with a USB C connector - you get what you pay for&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="problem-2--duplicate-node-ids"&gt;Problem 2 &amp;mdash; duplicate node IDs&lt;/h2&gt;
&lt;p&gt;Setting node 1 aside with its wiring still unresolved, I focused on getting nodes 2 and 3 reliably onto the bus. But the dashboard still only showed one node.&lt;/p&gt;
&lt;p&gt;The cause was embarrassingly simple. The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;NODE_ID&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in the firmware is a compile-time&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;#define&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#define NODE_ID    2 // 1 // 3 // change for appropriate node ID&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I had been flashing both nodes from the same file without updating the constant each time. Both nodes were broadcasting as node 2 on CAN ID&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x102&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;CAN bus arbitration is based on the message ID. When two nodes try to transmit the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;same CAN ID at the same time&lt;/strong&gt;, both think they are winning arbitration (the ID bits are identical so neither detects a collision in the arbitration phase) &amp;mdash; but their payload bytes differ. The result is a corrupted frame, which every node on the bus detects as an error. Error counters start climbing on all nodes, and the bus degrades.&lt;/p&gt;
&lt;p&gt;The fix is obvious in hindsight: flash each node with the correct ID.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;Node 1 → NODE_ID 1 → CAN ID 0x101
Node 2 → NODE_ID 2 → CAN ID 0x102
Node 3 → NODE_ID 3 → CAN ID 0x103&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="problem-3--all-nodes-transmitting-simultaneously"&gt;Problem 3 &amp;mdash; all nodes transmitting simultaneously&lt;/h2&gt;
&lt;p&gt;With unique IDs, both nodes came up and were visible on the dashboard. But after a minute or two, node 3 would drop off. Then reappear. Then drop off again.&lt;/p&gt;
&lt;p&gt;CAN bus handles collisions via&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;arbitration&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; when two nodes try to transmit at the same moment, the node with the lower ID wins and the other backs off and retries. This is elegant and normally fine. The problem is that my original code used a flat&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;delay(2000)&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in every node&amp;rsquo;s loop:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void loop() {
    delay(2000);
    // ... read sensor, transmit
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;If both nodes power up at roughly the same time (same power rail), they stay locked in sync and collide on every single cycle. CAN arbitration resolves each collision, but node 3 (higher CAN ID =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;0x103&lt;/code&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;em&gt;always loses&lt;/em&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; it backs off every single time. Under repeated arbitration losses the MCP2515 starts accumulating transmit errors in its internal&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;TEC (Transmit Error Counter)&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TEC &amp;gt; 96: warning threshold&lt;/li&gt;
&lt;li&gt;TEC &amp;gt; 127: error-passive (node still transmits but with passive error flags)&lt;/li&gt;
&lt;li&gt;TEC = 255:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;bus-off&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; node stops transmitting entirely&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fix: stagger the transmission timing per node so they are naturally out of phase with each other:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void loop() {
    // Stagger per node so they don&amp;#39;t all transmit simultaneously.
    // Base 2s interval + NODE_ID * 300ms offset keeps nodes out of phase.
    delay(2000 + NODE_ID * 300);
    // ...
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Node 2 now transmits every 2.6 s, node 3 every 2.9 s &amp;mdash; and when node 1 is eventually fixed, it will slot in at 2.3 s. They drift in and out of alignment over time but never stay locked together.&lt;/p&gt;
&lt;h2 id="problem-4--no-recovery-when-a-node-goes-bus-off"&gt;Problem 4 &amp;mdash; no recovery when a node goes bus-off&lt;/h2&gt;
&lt;p&gt;Even with staggered timing, occasional collisions can still accumulate errors, especially at 500 kbps with the MCP2515&amp;rsquo;s tight 8-time-quanta bit timing at 8 MHz. Once a node hits bus-off it stops transmitting and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;the original code had no recovery logic&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; the node would stay silent forever until manually reset.&lt;/p&gt;
&lt;p&gt;The fix: read the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;EFLG&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(Error Flag) register after any failed send and reset the chip if the bus-off bit is set:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;MCP2515::ERROR sendErr = mcp2515.sendMessage(&amp;amp;tx);
if (sendErr != MCP2515::ERROR_OK) {
    Serial.print(F(&amp;quot;ERR: send failed code=&amp;quot;)); Serial.println(sendErr);
    // EFLG bit 5 = TXBO (bus-off). Reset and re-enter normal mode.
    SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
    digitalWrite(MCP_CS_PIN, LOW);
    SPI.transfer(0x03); SPI.transfer(0x2D); // READ EFLG
    uint8_t eflg = SPI.transfer(0x00);
    digitalWrite(MCP_CS_PIN, HIGH);
    SPI.endTransaction();
    if (eflg &amp;amp; 0x20) {
        Serial.println(F(&amp;quot;EFLG: bus-off — resetting MCP2515&amp;quot;));
        mcp2515.reset();
        delay(100);  // give oscillator time to stabilise
        mcp2515.setBitrate(CAN_500KBPS, CAN_CLOCK);
        delay(10);
        mcp2515.setNormalMode();
    }
    return;
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Note the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;delay(100)&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;after reset &amp;mdash; the original code had only 10 ms here, which is sometimes not enough for the MCP2515&amp;rsquo;s oscillator to stabilise before you start writing configuration registers.&lt;/p&gt;
&lt;h2 id="seeing-errors-before-they-become-a-problem"&gt;Seeing errors before they become a problem&lt;/h2&gt;
&lt;p&gt;To understand what is happening on the bus in real time, I added a helper that reads and prints&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;EFLG&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;TEC&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;after every successful transmission, but only if something is non-zero (so normal output stays clean):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;// EFLG 0x2D: bit5=TXBO(bus-off) bit4=TXEP(err-passive) bit2=TXWAR(warning)
// TEC  0x1C: transmit error counter  REC 0x1D: receive error counter
void printErrors() {
    uint8_t eflg = readReg(0x2D);
    uint8_t tec  = readReg(0x1C);
    if (eflg == 0 &amp;amp;&amp;amp; tec == 0) return;
    Serial.print(F(&amp;quot; EFLG=0x&amp;quot;)); Serial.print(eflg, HEX);
    Serial.print(F(&amp;quot; TEC=&amp;quot;)); Serial.print(tec);
    if (eflg &amp;amp; 0x20) Serial.print(F(&amp;quot; [BUS-OFF]&amp;quot;));
    else if (eflg &amp;amp; 0x10) Serial.print(F(&amp;quot; [ERR-PASSIVE]&amp;quot;));
    else if (eflg &amp;amp; 0x01) Serial.print(F(&amp;quot; [WARNING]&amp;quot;));
    Serial.println();
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;With this in place you can watch TEC climbing before the node goes quiet &amp;mdash; it gives you a warning rather than a silent disappearance.&lt;/p&gt;
&lt;h2 id="what-i-learned"&gt;What I learned&lt;/h2&gt;
&lt;p&gt;Getting even two CAN nodes reliably talking to a master required solving four separate problems: a cold solder joint that looked fine visually, duplicate node IDs from a careless flash, synchronised timing that caused constant arbitration collisions, and no recovery from the bus-off state those collisions eventually caused. None of them were obvious until I had the right instrumentation in place. Node 1 remains a work in progress.&lt;/p&gt;
&lt;p&gt;The core lesson is that&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;CAN bus is robust but unforgiving about setup&lt;/strong&gt;: it handles arbitration and error detection in hardware, but the hardware needs clean wiring, unique IDs, and a firmware that knows how to recover from error states.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_node_5F00_not_5F00_helping_5F00_plant_5F00_much.jpg"  /&gt;&lt;/p&gt;
&lt;p&gt;Another thing I learned from my last Design Challenge is not to leave the build till too late. I had a fair bit of time to get these stands and circuit boards done. I was hoping to get more sensors on there, including the K-type thermocouples but some late shipment and a busy few weeks means all I got is a DHT11 temperature and humidity sensor.&lt;/p&gt;
&lt;h2 id="next"&gt;Next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fix node 1&amp;rsquo;s wiring&lt;/li&gt;
&lt;li&gt;maybe take some time out to actually grow some plants&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f606.svg" title="Laughing"&gt;&amp;#x1f606;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Hook it up to LabView&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a id="" href="https://github.com/saramic/green-brain" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/saramic/green-brain&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 8 - Interfacing Humidity and Temperature</title><link>https://community.element14.com/thread/57046?ContentTypeID=0</link><pubDate>Sun, 21 Jun 2026 05:43:13 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:4db1258e-89ee-4430-b12d-99fa48525283</guid><dc:creator>arvindsa</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57046?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57046/solarsense---part-8---interfacing-humidity-and-temperature/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqbvjvms0"&gt;Recap:&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master&amp;#39;s thesis of a minute Shape memory Alloy based solar panel cleaning robot&lt;/p&gt;
&lt;p&gt;Previous posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;SolarSense - Part 3 - PCB Schematics Walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation" data-e14adj="t"&gt;SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation&lt;/a&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation" data-e14adj="t"&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57030/solarsense---part-5---making-an-live-dashboard-using-lab-view" data-e14adj="t"&gt;SolarSense - Part 5 - Making an Live Dashboard using Lab View&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57039/solarsense---part-6---reading-the-heat-at-k-type-thermocouple-using-max31855" data-e14adj="t"&gt;SolarSense - Part 6 - Reading the Heat at K Type Thermocouple using MAX31855.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;S&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57041/solarsense---part-7---reading-the-sky---uv-sensor-interface-on-stm32" data-e14adj="t"&gt;olarSense - Part 7 - Reading the Sky - UV Sensor Interface on STM32&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Time is short, So i will jump straight into the points and avoid good formatting of this post. I have SEN66, BMP280 in my board and yet why am i using another sensor for Relative Humidity and Temperature?&amp;nbsp;&amp;nbsp;I needed something that could live away from the PCB, immune to internal heats etc,&amp;nbsp; a probe on a cable, pointed at the actual outdoor air, unaffected by what is happening inside the enclosure. The Omega HX94C is exactly that. It is an industrial-grade transmitter, NEMA 4 rated, with two completely independent 4-20 mA current loops: one for relative humidity (0&amp;ndash;100 %RH at 4&amp;ndash;20 mA) and one for temperature (0&amp;ndash;100 &amp;deg;C at 4-20 mA). You run a pair of wires out to it, excite the loop from your rail voltage, and measure the current. The Current based measurement means, length of wire will not matter, as opposed to voltage based measurement where the voltage will drop along the wire.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_094222_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260621_5F00_103531_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;I am using a&amp;nbsp;TPS61040DBVT boost converter to get a 12V, HX94C requires excitation voltage of 6-36V for its working. Next is choosing the shunt resistor to measure the full range of 4-20mA. At 150Ohm ressitor&lt;/p&gt;
&lt;p&gt;At&amp;nbsp; 4 mA: V = 0.004 &amp;times; 150 = 0.60 V&lt;br /&gt;At 20 mA: V = 0.020 &amp;times; 150 = 3.00 V&lt;/p&gt;
&lt;p&gt;This is within the range of STM32&amp;#39;s ADC&lt;/p&gt;
&lt;h2 id="mcetoc_1jrkanufe0"&gt;The Firmware&lt;/h2&gt;
&lt;p&gt;Just like my previous post of the UV sensor, I am simply reading the ADC values for both using&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static HAL_StatusTypeDef adc_read_mv(HX94C_t *dev, uint32_t channel, uint32_t *mv)
{
    ADC_ChannelConfTypeDef c = {0};
    c.Channel      = channel;
    c.Rank         = ADC_REGULAR_RANK_1;
    c.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;  /* high source impedance */
    c.SingleDiff   = ADC_SINGLE_ENDED;
    c.OffsetNumber = ADC_OFFSET_NONE;

    HAL_StatusTypeDef st = HAL_ERROR;
    if (HAL_ADC_ConfigChannel(dev-&amp;gt;hadc, &amp;amp;c) == HAL_OK &amp;amp;&amp;amp;
        HAL_ADC_Start(dev-&amp;gt;hadc) == HAL_OK)
    {
        if (HAL_ADC_PollForConversion(dev-&amp;gt;hadc, ADC_TIMEOUT_MS) == HAL_OK)
        {
            uint32_t raw = HAL_ADC_GetValue(dev-&amp;gt;hadc);
            *mv = (raw * HX94C_VDDA_MV) / ADC_FULL_SCALE;
            st = HAL_OK;
        }
        HAL_ADC_Stop(dev-&amp;gt;hadc);
    }
    return st;
}&lt;/pre&gt;\&lt;/p&gt;
&lt;p&gt;Once I have the millivolt reading from the shunt, the math is straightforward:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/* I(&amp;#181;A) = V(mV) / Rsense(Ω) &amp;#215; 1000 */
int32_t i_rh = (int32_t)(rh_mv * 1000U / HX94C_RSENSE_OHMS);
int32_t i_t  = (int32_t)(t_mv  * 1000U / HX94C_RSENSE_OHMS);&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrkb1iau1"&gt;The results&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:900px;"  src="https://community.element14.com/resized-image/__size/1800x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_21_5F00_10_2D00_16_2D00_56.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The HX94C contributes six tokens: hx_ok (both loops healthy), hx_rh (tenths of a %RH), hx_t (centi-&amp;deg;C), hx_rhi / hx_ti (the two loop currents in tenths of a mA, kept for diagnostics) and hx_flt (the fault bitmask). Decoding the HX94C fields across those samples:&amp;nbsp;&lt;/p&gt;
&lt;figure class="table-figure"&gt;
&lt;table border="1"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;hb&lt;/th&gt;
&lt;th&gt;RH loop&lt;/th&gt;
&lt;th&gt;RH&lt;/th&gt;
&lt;th&gt;Temp loop&lt;/th&gt;
&lt;th&gt;Temp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;15.5 mA&lt;/td&gt;
&lt;td&gt;71.7 %&lt;/td&gt;
&lt;td&gt;8.3 mA&lt;/td&gt;
&lt;td&gt;26.8 &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;15.7 mA&lt;/td&gt;
&lt;td&gt;72.8 %&lt;/td&gt;
&lt;td&gt;8.4 mA&lt;/td&gt;
&lt;td&gt;27.8 &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;15.0 mA&lt;/td&gt;
&lt;td&gt;68.7 %&lt;/td&gt;
&lt;td&gt;8.7 mA&lt;/td&gt;
&lt;td&gt;29.2 &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;15.5 mA&lt;/td&gt;
&lt;td&gt;72.1 %&lt;/td&gt;
&lt;td&gt;8.3 mA&lt;/td&gt;
&lt;td&gt;27.0 &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;
&lt;p&gt;BTW, hb is heartbeat that comes every 5second&lt;/p&gt;
&lt;h2 id="mcetoc_1jrkb6bqd2"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;It&amp;#39;s time to submit project, and Will invest time in writing the final post. Feel free to ask questions if i missed anything.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Green Brain - Part III - Water Cannon Chaos</title><link>https://community.element14.com/thread/57045?ContentTypeID=0</link><pubDate>Sat, 20 Jun 2026 14:39:25 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:dc56b522-7378-4dcb-946f-c067cc5d45ec</guid><dc:creator>saramic</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57045?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57045/green-brain---part-iii---water-cannon-chaos/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;I promised a water cannon&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f4a6.svg" title="Sweat drops"&gt;&amp;#x1f4a6;&lt;/span&gt;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f52b.svg" title="Gun"&gt;&amp;#x1f52b;&lt;/span&gt;&amp;nbsp;for my plants, so I had to deliver, if only for a forum post.&lt;/p&gt;
&lt;h1 id="recap"&gt;Recap&lt;/h1&gt;
&lt;p&gt;Green Brain&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f33f.svg" title="Herb"&gt;&amp;#x1f33f;&lt;/span&gt;&amp;nbsp;is the idea of tracking plant nodes via an industrial&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;CAN bus&lt;/strong&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to ultimately see which plant needs more water cannon&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f52b.svg" title="Gun"&gt;&amp;#x1f52b;&lt;/span&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/56921/green-brain---part-i---can-bus-introduction" data-e14adj="t"&gt;Green Brain - Part I - CAN bus introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57001/green-brain---part-ii---dev-setup" data-e14adj="t"&gt;Green Brain - Part II - Dev setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-idea-and-the-gear"&gt;the idea and the gear&lt;/h2&gt;
&lt;p&gt;I had the gear for a water cannon. My wife wanted an indoor plant watering system for Christmas so I got her one. But what present is complete without some extra DIY in case I could build one myself. As such I had a few small pumps around. I also had a servo driven X-Y pan and tilt module. How hard can it be to hook that up and make a water cannon.&lt;/p&gt;
&lt;p&gt;As an aside, I am also super grateful for a bunch of gear that Element 14 sent to me as a selected challenger.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260620_5F00_on_5F00_the_5F00_line_5F00_challengers_5F00_kit.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The pressure is on to use as much of this gear as I can.&lt;/p&gt;
&lt;h2 id="water-and-electronics"&gt;Water and electronics&lt;/h2&gt;
&lt;p&gt;Well there was a lot of learning to be had:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;no matter how well you think you seal things - water will still leak. Although I didn&amp;rsquo;t go all out with silicon, I thought that tight fitting vinyl tubing, electrical tape and relatively low water pressure would be OK &amp;mdash; it was not&lt;/li&gt;
&lt;li&gt;Water brings in fluid dynamics and things like head, flow, nozzle shapes and the like. Clearly my pump could not initially pump up 300mm of head so I moved the motor closer to the nozzle. There would also be time required to refill the tubing up to the nozzle, as once the pump stops the tube empties. Gardening nozzles from a local hardware are only so good at firing a stream of water&lt;/li&gt;
&lt;li&gt;Even small hoses can be stiff enough to move the servos&lt;/li&gt;
&lt;li&gt;Pan and Tilt movement requires some planning in how everything interacts. My platform got tangled in the hose, and the wiring also got stuck every so often for the servos as everything moved around&lt;/li&gt;
&lt;li&gt;Water is heavy and the motion of water in a hose can change the weight balance of everything and move things around&lt;/li&gt;
&lt;li&gt;With all this chaos, I was not bringing the UNO Q anywhere near this water cannon&lt;/li&gt;
&lt;li&gt;Noise, pump motors can produce noise, and that noise can be picked up by the servos and move them haywire around the place&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It was this last point that really killed this feature. I had removed the servo library entirely, moved the pump power onto a completely separate circuit isolated by a relay &amp;mdash; and still the servos would pan to the right and tilt to the top. Stripping down to a pure diagnostic sketch (no servo code at all) revealed the culprit: the servo signal pins were floating high-impedance inputs, acting as antennas and picking up the pump motor&amp;rsquo;s EMI directly? maybe? &lt;strong&gt;&lt;span style="color:#333399;font-family:&amp;#39;courier new&amp;#39;, courier;"&gt;&amp;macr;_(ツ)_/&amp;macr;&lt;/span&gt;&lt;/strong&gt;. Even with no code driving them, the servo controllers saw enough noise to chase a position. I ran out of steam getting this to behave reliably with the full mechanical assembly.&lt;/p&gt;
&lt;p style="display:flex;width:100%;"&gt;&lt;img loading="lazy" alt="image" style="cursor:zoom-in;max-height:360px;max-width:49%;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260620_5F00_pan_5F00_and_5F00_tilt_5F00_cannon.gif" /&gt; &lt;img loading="lazy" alt="image" style="cursor:zoom-in;max-height:360px;max-width:49%;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260620_5F00_water_5F00_cannon_5F00_shoot_5F00_right_5F00_and_5F00_up.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://community.element14.com/cfs-file/__key/communityserver-discussions-components-files/453/20260620_5F00_palying_5F00_around_5F00_with_5F00_firing_5F00_the_5F00_cannon.mp4"&gt;community.element14.com/.../20260620_5F00_palying_5F00_around_5F00_with_5F00_firing_5F00_the_5F00_cannon.mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was hoping to re-use the water cannon as a dog deterrent from scratching a door. I think neither the dogs nor the plants will be seeing any water cannon any time soon.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="cursor:zoom-in;display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260620_5F00_water_5F00_cannon.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The only clever things I did was write to EEProm to see if a reboot was happening when the pump kicked in and use the Omega temperature probe to hold the tube that fed water to the pump.&lt;/p&gt;
&lt;h2 id="next"&gt;Next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I have some MAX6675 which would be cool to connect to the K-Type Thermocouples&lt;/li&gt;
&lt;li&gt;there is the Omega Temperature probe&lt;/li&gt;
&lt;li&gt;and I am hoping to at least visualise a little bit of this on NI LabView&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;oh and there is like 24 hours left&amp;nbsp;&lt;span class="emoticon" data-url="https://community.element14.com/cfs-file/__key/system/emoji/1f91e.svg" title="Fingers crossed"&gt;&amp;#x1f91e;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="source"&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a id="" href="https://github.com/saramic/green-brain" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://github.com/saramic/green-brain&lt;/a&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>5 RPC power conditioning</title><link>https://community.element14.com/thread/57044?ContentTypeID=0</link><pubDate>Sat, 20 Jun 2026 01:40:24 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:c6ef44f6-879b-44a8-b084-2d98c61fd08d</guid><dc:creator>pandoramc</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57044?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57044/5-rpc-power-conditioning/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h1 id="mcetoc_1jrha9h1m1"&gt;Introduction&lt;/h1&gt;
&lt;p style="text-align:left;"&gt;The cooling system is not only temperature dependable, but the system also requires a device that allows temperature reduction in fluids mainly. This device is known as compressor, it takes a refrigerant gas in a closed loop and circulates it throughout a pneumatic circuit. Additional components are necessary in the circuit because compression and decompression can increase or decrease the temperature of the interchange chamber. The challenge here now is how to use a low-level digital signal to activate a high-power AC load. The solution is a solid-state relay (SSR).&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}Design Platform&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img style="max-height:360px;max-width:640px;" alt="Digital System" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260514_5F00_225033.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Compressor" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/cooling.jpeg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 1. Design platform&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrha9ckj0"&gt;Methodology&lt;/h2&gt;
&lt;p&gt;Why use an SSR compared with an electromechanical relay (EMR)?&lt;/p&gt;
&lt;p style="text-align:left;"&gt;The EMR requires a specific power supply such as 5VCD or 12VCD. In the Arduino UNO Q system, the digital terminals are controlled by the microcontroller system. According to the datasheet, some I/O pins are 5VCD tolerant, but they only generate up to 4VCD, which voltage is not sufficient to activate the relay. As you may wonder, is there any option to activate the EMC with lower voltage? There are EMC in the market that activates the coil with 3VCD with a 25Ω internal resistance, consequently, it is possible to use this kind of relays. On the other hand, the I/O has a limit of 20 mA maximum of current to be sourced; the 3VCD@25 Ω consumes 120 mA, this means that the I/O pin of the microcontroller will be damaged operating in these conditions. Additionally, the AC loads could create an electric arc when the load is disconnected from the mechanical switch. This arc could discharge in the control signal and damage all the system, requiring isolation or anti arc PCB for high-current loads.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}I/O features&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Voltage Features" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Voltage.png" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Current Features" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Current.png" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 2. Microcontroller I/O characteristics&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The assembled compressor has &amp;frac14; HP, which it represents a load of 186.5 W for a pure resistive load. But the compressor has additional features like inductive and capacitive behavior. This means that a compressor requires a better switch capacity according to the power factor (PF). I considered a PF=0.5, this means that the current will be about 3A@120VAC. An EMC of 12VCD can handle a load of 10A@125VAC but it requires an additional power supply in the system. In order to minimize the possible arc generation derived of switching and avoiding additional components in the system for power conditioning, an SSR is a better option. The SSR for AC loads has semiconductor elements to reach high commutation frequency and these elements reduce the arc generation risk for the power required in this system. An additional feature is the isolation between control and power stages; it allows easy maintenance for the systems and ensures security.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt="SSR schematic" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/SSR.png" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 3. SSR internals (FOTEK SSR Series datasheet)&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align:left;"&gt;I implemented the example of blink LED using the RPC interface changing the digital pin 12. This perspective was used with the SSR since the example creates a web user interface by WebUI Arduino brick to turn on/turn off the SSR with a 3V3 digital control signal.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}SSR test&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="SSR turned off" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260618_5F00_175623.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="SSR turned on" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260618_5F00_175645.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="SSR final assembly" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260618_5F00_190034.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 4. SSR test and heat sink assembly&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jrhbmn2n0"&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#include &amp;lt;Arduino_RouterBridge.h&amp;gt;

void setup() {
    pinMode(D12, OUTPUT);
    // Start with the LED OFF (HIGH state of the PIN)
    digitalWrite(D12, HIGH);

    Bridge.begin();
    Bridge.provide(&amp;quot;set_led_state&amp;quot;, set_led_state);
}

void loop() {}

void set_led_state(bool state) {
    // LOW state means LED is ON
    digitalWrite(D12, state ? LOW : HIGH);
}&lt;/pre&gt;&lt;/h1&gt;
&lt;h1 id="mcetoc_1jrhamueg2"&gt;The future&lt;/h1&gt;
&lt;p&gt;Once the construction blocks are ready, it is time to implement the monitoring system and test the data sharing and control-aided cooling system. At this stage, it is possible to view the data on the UT325F thermometer and capture the data in the Arduino UNO Q for storage. The monitoring system must be implemented in order to view the data using the same web interface system. On the other hand, I had problems with serial port access in the Arduino App Lab, but there are other options such as Inter Process Communication (IPC) to create a data sharing service in the network.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt="Assembled system" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260618_5F00_081618.jpg" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 5. Isolated monitoring cooling system&lt;/em&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;script&gt;window.top.e14.func.queueScripts.add(function() { window.top.e14.func.e14DynaloadGallery(window.document);}, true );&lt;/script&gt;</description></item><item><title>Friendly Reminder - On The Line Build Period Ends This Sunday</title><link>https://community.element14.com/thread/57043?ContentTypeID=0</link><pubDate>Fri, 19 Jun 2026 15:29:02 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:91548fea-a207-473b-b4ee-30157bc16805</guid><dc:creator>JoRatcliffe</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57043?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57043/friendly-reminder---on-the-line-build-period-ends-this-sunday/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;Hi all, just a quick reminder that the deadline for &lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/b/projects" data-e14adj="t"&gt;final project write-ups&lt;/a&gt; is midnight UK time this Sunday (21st June) so you have just a couple more days to go.&lt;/p&gt;
&lt;p&gt;Have a good weekend and looking forward to seeing everyone&amp;#39;s final updates before the build period of this design challenge ends!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Making a CAN Network</title><link>https://community.element14.com/thread/57042?ContentTypeID=0</link><pubDate>Fri, 19 Jun 2026 12:47:28 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:8ffed8e4-2aba-4a7d-b09b-a62af89a2785</guid><dc:creator>taifur</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57042?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57042/making-a-can-network/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p class="isSelectedEnd"&gt;&lt;span&gt;Controller Area Network (CAN) is a robust, reliable, and real-time communication protocol widely used in industrial automation and embedded systems. CAN enables multiple&amp;nbsp;nodes to communicate over a single two-wire bus without requiring any central host. A CAN node can be any independent sensor or actuator unit. CAN networks are extensively used in process automation, industrial machinery, and robotic systems. It has very high noise immunity and requires only two wires for communication.&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;My project involves a CAN network where there will be an ESP32-based CAN node and a CAN gateway based on Arduino UNO Q and&amp;nbsp;MAX33041E CAN transceiver shield from Analog Devices.&amp;nbsp;The ESP32-based node collects sensor data and transmits it over the CAN bus to&amp;nbsp;the central CAN gateway.&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;The ESP32 has a built-in CAN bus-compatible controller as TWAI which stands for Two-Wire Automotive Interface, it doesn&amp;rsquo;t have a built-in CAN transceiver, so we must use an external one to connect to a CAN network. So, in my project, I am using the MCP2551&amp;nbsp;CAN transceiver&amp;nbsp;module to make a CAN node using an ESP32 microcontroller.&amp;nbsp;The MCP2551&amp;nbsp; transceiver converts the CAN controller&amp;#39;s digital TX/RX signals into the differential CANH/CANL bus signals.&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;I connected the&amp;nbsp;VCC pin of the MCP2551 module to the VIN&amp;nbsp;of&amp;nbsp;the ESP32. Then, connect the GND pin to the ground. Next, connect the TX to GPIO5 and the RX to GPIO4 on the ESP32.&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;&lt;img alt="image" style="max-height:522px;max-width:696px;"  height="522" src="https://community.element14.com/resized-image/__size/1392x1044/__key/communityserver-discussions-components-files/453/IMG_5F00_3931.JPG" width="696" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;The next step is to connect the CAN Bus High and CAN Bus Low signals to the bus. I connected the CANH and CANL signals directly to the screw terminal on the&amp;nbsp;&lt;a id="e14-product-link-635ca" class="e14-embedded e14_shopping-cart-far e14-link" href="https://referral.element14.com/OrderCodeView?fsku=3807530&amp;amp;nsku=73AJ8377&amp;amp;COM=e14c-e14-on-the-line&amp;amp;CMP=e14c-e14-on-the-line&amp;amp;osetc=e14c-e14-on-the-line" data-at-areainteracted="rte-content" data-at-type="click" data-at-link-type="link" data-at-label="PRODUCT_POPUP_OPEN" data-farnell="3807530" data-newark="73AJ8377" data-comoverride="e14-on-the-line" data-cmpoverride="e14-on-the-line" data-cpc="" data-avnetemea="" data-avnetema="" data-avnetasia="" target="_blank" data-e14adj="t"&gt;MAX33041 Shield Evaluation Kit&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:522px;max-width:696px;"  height="522" src="https://community.element14.com/resized-image/__size/1392x1044/__key/communityserver-discussions-components-files/453/IMG_5F00_3936.JPG" width="696" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;For programming the ESP32 module for CAN communication, I installed the following library:&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/mcp2515.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;For sending the test message on the CAN bus, I used the following test code.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class="isSelectedEnd"&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="text"&gt;#include &amp;lt;CAN.h&amp;gt;

#define TX_GPIO_NUM   5
#define RX_GPIO_NUM   4

void setup() {
  Serial.begin (115200);
  while (!Serial);
  delay (1000);

  Serial.println (&amp;quot;CAN Sender&amp;quot;);

  // Set the pins
  CAN.setPins (RX_GPIO_NUM, TX_GPIO_NUM);

  // start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println(&amp;quot;Starting CAN failed!&amp;quot;);
    while (1);
  }
}

void loop() {
  // send packet: id is 11 bits, packet can contain up to 8 bytes of data
  Serial.print(&amp;quot;Sending packet ... &amp;quot;);

  CAN.beginPacket(0x12);
  CAN.write(&amp;#39;h&amp;#39;);
  CAN.write(&amp;#39;e&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;o&amp;#39;);
  CAN.endPacket();

  Serial.println(&amp;quot;done&amp;quot;);

  delay(1000);

  // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
  Serial.print(&amp;quot;Sending extended packet ... &amp;quot;);

  CAN.beginExtendedPacket(0xabcdef);
  CAN.write(&amp;#39;w&amp;#39;);
  CAN.write(&amp;#39;o&amp;#39;);
  CAN.write(&amp;#39;r&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;d&amp;#39;);
  CAN.endPacket();

  Serial.println(&amp;quot;done&amp;quot;);

  delay(1000);
}&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 7 - Reading the Sky - UV Sensor Interface on STM32</title><link>https://community.element14.com/thread/57041?ContentTypeID=0</link><pubDate>Fri, 19 Jun 2026 09:24:06 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:d7862279-2d00-4935-8d36-ebb0de51f5c7</guid><dc:creator>arvindsa</dc:creator><slash:comments>5</slash:comments><comments>https://community.element14.com/thread/57041?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57041/solarsense---part-7---reading-the-sky---uv-sensor-interface-on-stm32/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqbvjvms0"&gt;Recap:&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master&amp;#39;s thesis of a minute Shape memory Alloy based solar panel cleaning robot&lt;/p&gt;
&lt;p&gt;Previous posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;SolarSense - Part 3 - PCB Schematics Walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation" data-e14adj="t"&gt;SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57030/solarsense---part-5---making-an-live-dashboard-using-lab-view" data-e14adj="t"&gt;SolarSense - Part 5 - Making an Live Dashboard using Lab View&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57039/solarsense---part-6---reading-the-heat-at-k-type-thermocouple-using-max31855" data-e14adj="t"&gt;SolarSense - Part 6 - Reading the Heat at K Type Thermocouple using MAX31855.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1jrfhuult0"&gt;Why UV?&lt;/h2&gt;
&lt;p&gt;The obvious answer you might think is &amp;quot;because UV degrades solar panels like so many other things.&amp;quot; And yes, that is absolutely true. UV radiation is the primary driver of photovoltaic cell degradation over years of operation. The casing yellows, the anti-reflection coating breaks down, Tracking cumulative UV dose over time gives a solid predictor for long-term efficiency loss that has nothing to do with dust.&lt;br /&gt;&lt;br /&gt;But for this project, That would not be the answer, UV degradation is established fact and there is no point in doing any research on it. But what is interesting is that&amp;nbsp; Solar irradiance (energy per unit area) and UV index do not move in together. Clouds scatter and absorb visible light far more aggressively than they block UV &amp;mdash; there are days where the sky looks completely overcast and the UV index is still punishing. The panels see almost no visible light but a significant UV dose. If my model only tracks panel output and misses UV, it cannot distinguish &amp;quot;sky is genuinely dark&amp;quot; from &amp;quot;sky is bright but the panel surface is blocked.&amp;quot; UV gives the model context about what the actual sky condition is, independent of what the panels are producing.&lt;br /&gt;&lt;br /&gt;The key insight: UV index is a proxy for the radiative environment that survives cloud cover in a way that visible irradiance does not. That independence is exactly what makes it useful as a model input. Now, I have to emphasize that the UV is not an authoritative indicator for passing clouds etc but it will be helpful.&lt;/p&gt;
&lt;h2 id="mcetoc_1jrfhuult1"&gt;What the GUVA Sensor Actually Does&lt;/h2&gt;
&lt;p&gt;The GUVA-S12SD is a GaN-based Schottky-type photodiode that is sensitive from roughly 240 nm to 370 nm. That range covers UV-B (280&amp;ndash;315 nm) and most of UV-A, but the response peaks around 360 nm and falls to near zero before 400 nm &amp;mdash; so visible light is well and truly rejected. The sensor is genuinely seeing UV, not just acting as a general light sensor with a blue filter.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/20260619_5F00_123156_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;What the chip itself produces is a current proportional to incident UV intensity. The datasheet specifies a responsivity of 0.14 A/W at 300 nm and a photocurrent of 26 nA per UV index unit under sunlight conditions. On the Adafruit breakout module, this current feeds into an op-amp circuit that amplifies it to a usable voltage level. &lt;a href="https://www.adafruit.com/product/1918" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;Adafruit&amp;rsquo;s documentation &lt;/a&gt;states the conversion as:&amp;nbsp;&lt;code&gt;V_out = 4.3 &amp;times; I_photodiode_in_&amp;micro;A&lt;/code&gt;&lt;/p&gt;
&lt;table class="table table-striped table-bordered" border="1"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Approximate V_out&lt;/th&gt;
&lt;th&gt;UV Index&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dark / indoors&lt;/td&gt;
&lt;td&gt;0.05 &amp;ndash; 0.15 V&lt;/td&gt;
&lt;td&gt;0 &amp;ndash; 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overcast outdoor&lt;/td&gt;
&lt;td&gt;0.15 &amp;ndash; 0.40 V&lt;/td&gt;
&lt;td&gt;1 &amp;ndash; 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hazy sun&lt;/td&gt;
&lt;td&gt;0.40 &amp;ndash; 0.80 V&lt;/td&gt;
&lt;td&gt;4 &amp;ndash; 8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear noon Kerala summer&lt;/td&gt;
&lt;td&gt;0.90 &amp;ndash; 1.40 V&lt;/td&gt;
&lt;td&gt;9 &amp;ndash; 14&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class="has-line-data" data-line-start="48" data-line-end="49"&gt;My 0.3 V reading at test time is UV Index 3&amp;nbsp; exactly right for indoors near a window. The formula to compute UV index from the output voltage is simply dividing by 0.1 V, or in firmware:&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="48" data-line-end="49"&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/* UV index in tenths from GUVA output voltage.
   Per Adafruit: UV index = V_out / 0.1 V  →  tenths = uv_mv / 10 */
uint32_t uv_idx10 = uv_mv / 10U;&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrfi88bn2" class="code-line has-line-data" data-line-start="77" data-line-end="78"&gt;The Code&lt;/h2&gt;
&lt;p class="has-line-data" data-line-start="79" data-line-end="80"&gt;There are two pieces of setup required before any ADC conversion can be usable on the STM32L4. The first is the calibration call unlike older STM32 families, the L4&amp;rsquo;s ADC has an internal calibration routine that must be run once at startup before the first conversion. It writes offset correction values to the ADC&amp;rsquo;s internal registers and takes only a few microseconds. Without it, readings come out inaccurate. This goes right after &lt;code&gt;MX_ADC1_Init()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="has-line-data language-c" data-line-start="82" data-line-end="84"&gt;HAL_ADCEx_Calibration_Start(&amp;amp;hadc1, ADC_SINGLE_ENDED);&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class="has-line-data" data-line-start="85" data-line-end="86"&gt;After that, reading the UV channel each telemetry tick is straightforward. The ADC is in scan mode with four ranks, which means all four channels are sequenced on every trigger &amp;mdash; you cannot read just rank 1 in isolation. You have to drain all four values or the next conversion starts out of step.&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="85" data-line-end="86"&gt;The Conversion Rank in CubeMX&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="85" data-line-end="86"&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_19_5F00_14_2D00_40_2D00_44.png" /&gt;&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="85" data-line-end="86"&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/* UV sensor (GUVA analog) on ADC1 rank 1 (ADC_CHANNEL_5 / PA0).
   Scan group has 4 ranks; all must be drained on every conversion. */
if (o &amp;lt; (int)sizeof(tlm) - 16)
{
    uint32_t uv_raw = 0;
    if (HAL_ADC_Start(&amp;amp;hadc1) == HAL_OK)
    {
        if (HAL_ADC_PollForConversion(&amp;amp;hadc1, 10) == HAL_OK)
            uv_raw = HAL_ADC_GetValue(&amp;amp;hadc1);
        /* Drain ranks 2-4 (HX94C RH, HX94C TEMP, RAIN AO) */
        for (int r = 1; r &amp;lt; 4; r++)
        {
            if (HAL_ADC_PollForConversion(&amp;amp;hadc1, 10) == HAL_OK)
                (void)HAL_ADC_GetValue(&amp;amp;hadc1);
        }
        HAL_ADC_Stop(&amp;amp;hadc1);
    }
    uint32_t uv_mv = uv_raw * 3300U / 4095U;
    o += snprintf(tlm + o, sizeof(tlm) - o, &amp;quot; uv_mv=%lu&amp;quot;, (unsigned long)uv_mv);
}&lt;/pre&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrfijuok5" class="has-line-data" data-line-start="85" data-line-end="86"&gt;The Readings&lt;/h2&gt;
&lt;p&gt;Just Like in the measurement of the temperature at the thermocouple, I do not have a calibrated UV source or measurement system. So I am going to just see if the multi-meter reading of signal output is same as what STM32 picks up.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260619_5F00_122852_2800_1_2900_.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_19_5F00_12_2D00_29_2D00_11.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrfigf2i4" class="code-line has-line-data" data-line-start="154" data-line-end="155"&gt;Final Notes&lt;/h2&gt;
&lt;p class="has-line-data" data-line-start="156" data-line-end="157"&gt;One line of calibration code. That is the whole lesson here. &lt;code&gt;HAL_ADCEx_Calibration_Start&lt;/code&gt; on the STM32L4 is not optional.&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="158" data-line-end="159"&gt;The UV sensor is live and reading sensibly. Next I want to run it through a full day outdoors and plot the curve rising from low values before sunrise, peaking around local solar noon, dropping back. If the curve shape looks right and correlates with what I would expect for the season and latitude, I will be satisfied that the sensor is working correctly.&lt;/p&gt;
&lt;p class="has-line-data" data-line-start="160" data-line-end="161"&gt;After that, the environmental sensors&amp;nbsp; BMP280 and the HX94C are next in the queue. The ADC scan group already has the HX94C analog channels wired up, it is just waiting for code similar to what I did here. The sensing stack is getting close to complete. I am yet to solder the BMP280. I am hesitant because deep down in my heart, It feels like pressure is not going to be an useful parameter.&lt;/p&gt;
&lt;pre&gt;&lt;code class="has-line-data language-c" data-line-start="82" data-line-end="84"&gt;&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 6 - Reading the Heat at K Type Thermocouple using  MAX31855.</title><link>https://community.element14.com/thread/57039?ContentTypeID=0</link><pubDate>Fri, 19 Jun 2026 06:24:41 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:18f2eef9-b6b0-46ba-af8f-e12400b887f3</guid><dc:creator>arvindsa</dc:creator><slash:comments>0</slash:comments><comments>https://community.element14.com/thread/57039?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57039/solarsense---part-6---reading-the-heat-at-k-type-thermocouple-using-max31855/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqo25snc0" style="text-align:left;"&gt;Recap&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master&amp;#39;s thesis of a minute Shape memory Alloy based solar panel cleaning robot&lt;/p&gt;
&lt;p&gt;Previous posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;SolarSense - Part 3 - PCB Schematics Walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;&lt;/a&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation" data-e14adj="t"&gt;SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57030/solarsense---part-5---making-an-live-dashboard-using-lab-view" data-e14adj="t"&gt;SolarSense - Part 5 - Making an Live Dashboard using Lab View&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1jrf6bmhg1"&gt;Why a Junction Thermocouple?&lt;/h2&gt;
&lt;p&gt;If you read Part 1, you would have seen that in the Proof of Concept I was already using the SEN66 for ambient temperature. And going forward I am planning to use the Omega HX94C sensor for temperature and relative humidity &amp;mdash; it is a more accurate sensor for ambient conditions and handles the Kerala humidity far better.&lt;/p&gt;
&lt;p&gt;So then, why do I also have a thermocouple on each panel? Because a thermocouple measures surface temperature, not ambient. What I want to know is how hot the panel itself gets, not the air around it. The two are very different. A panel sitting under direct sun in Kerala can be 20&amp;ndash;30&amp;deg;C hotter than the air temperature. That heat is not just a background condition, it directly affects the panel&amp;#39;s output. Crystalline silicon panels lose roughly 0.4% of their output per degree Celsius of cell temperature rise. Over a full day of sun exposure that is a measurable and significant drop.&lt;/p&gt;
&lt;p&gt;The key insight: I am not just monitoring soiling. I am studying what long-term heat exposure does to each panel&amp;#39;s efficiency over time. If one panel consistently runs hotter than the others.&amp;nbsp; maybe due to its mounting position, or partial shade that creates hotspot effects&amp;nbsp; that shows up in the data as a slow efficiency drift that has nothing to do with dust. Without the surface temperature measurement I would not be able to separate that from actual soiling. Now, these are just my hypothesis, and hope to get it proven. What better way to make use of the&amp;nbsp;&lt;a id="e14-product-link-1fd9f" class="e14-embedded e14_shopping-cart-far e14-link" href="https://referral.element14.com/OrderCodeView?fsku=3795213&amp;amp;nsku=30AC8630&amp;amp;COM=e14c-e14-on-the-line&amp;amp;CMP=e14c-e14-on-the-line&amp;amp;osetc=e14c-e14-on-the-line" data-at-areainteracted="rte-content" data-at-type="click" data-at-link-type="link" data-at-label="PRODUCT_POPUP_OPEN" data-farnell="3795213" data-newark="30AC8630" data-comoverride="e14-on-the-line" data-cmpoverride="e14-on-the-line" data-cpc="" data-avnetemea="" data-avnetema="" data-avnetasia="" target="_blank" data-e14adj="t"&gt;OmegaDwyer 5SRTC-TT-K-24-72 Thermocouple, K, 0 &amp;deg;C, 260 &amp;deg;C, 80 &amp;quot;, 2 m (Pack of 5)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The PCB has three MAX31855KASA+ thermocouple amplifier chips, one per panel with K-type thermocouple inputs. For now, I am going to solder only one chip. I actually found that the module is cheaper than the chip itself, so I just harvested it from the module.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/20260619_5F00_105230.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrf6hm8l3"&gt;Interfacing with MAX31855&lt;/h2&gt;
&lt;p&gt;The MAX31855 is a single-chip thermocouple amplifier with SPI output. You wire a K-type thermocouple to the two input pins, and the chip handles the cold-junction compensation internally using a temperature sensor on the die itself. What comes out of the SPI port is a fully compensated temperature reading. No lookup tables, no Seebeck coefficient math in firmware. The chip does it all.&lt;/p&gt;
&lt;p&gt;The catch? It is completely read-only. There are no configuration registers. No setup. No init sequence. You pull CS low, clock out 32 bits, release CS, and you have your reading. That is the entire protocol.&lt;/p&gt;
&lt;p&gt;Those 32 bits break down like this:&lt;/p&gt;
&lt;figure class="table-figure"&gt;&lt;/figure&gt;
&lt;p&gt;The three fault bits in [2:0] tell you exactly what is wrong with the thermocouple wiring. OC means nothing is plugged in. SCG and SCV mean the wire is touching something it should not. When any of these are set, bit 16 goes high and the thermocouple reading in [31:18] is cleared to zero. Here is the Descriptions from the Datasheet&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/pastedimage1781848430986v2.png" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrf6ojme5"&gt;Touching a Dusty Part of CubeMX and Extracting Data from SPI Frame&lt;/h2&gt;
&lt;p&gt;Usually, in CubeMX I rarely configure settings of SPI, I just make it the default settings of 8Bits per SPI frame.But 32Bits is frame size?&amp;nbsp; My LORA Module required 16bits frame. So either make it 32bus for now, then manage it when i actually start using lora or I can make it future proof now. So, Math Says&amp;nbsp;32 bits is just two 16-bit transfers. HAL can do two back to back with CS held low. No reconfiguration needed, no disruption to the lora driver. and I had to keep the SPI Clock speed at 5Mhz as limited by the MAX31855. To set these, I went to a part of CubeMX where I have rarely been.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_19_5F00_11_2D00_57_2D00_001.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;This settings translate to&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;hspi1.Instance               = SPI1;
hspi1.Init.Mode              = SPI_MODE_MASTER;
hspi1.Init.Direction         = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize          = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity       = SPI_POLARITY_LOW;   /* CPOL = 0 */
hspi1.Init.CLKPhase          = SPI_PHASE_1EDGE;    /* CPHA = 0 */
hspi1.Init.NSS               = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; /* 80MHz/16 = 5MHz */
hspi1.Init.FirstBit          = SPI_FIRSTBIT_MSB;
HAL_SPI_Init(&amp;amp;hspi1);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The actual read in the driver then looks like this:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static HAL_StatusTypeDef read_raw(MAX31855_t *dev, uint32_t *raw)
{
  uint16_t w[2];
  HAL_GPIO_WritePin(dev-&amp;gt;cs_port, dev-&amp;gt;cs_pin, GPIO_PIN_RESET);
  HAL_StatusTypeDef st = HAL_SPI_Receive(dev-&amp;gt;hspi, (uint8_t *)w, 2, SPI_TIMEOUT_MS);
  HAL_GPIO_WritePin(dev-&amp;gt;cs_port, dev-&amp;gt;cs_pin, GPIO_PIN_SET);
  if (st == HAL_OK)
    *raw = ((uint32_t)w[0] &amp;lt;&amp;lt; 16) | w[1];
  return st;
}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;For the Thermocouple temperature i used the code to extract the data from the SPI Frame&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int32_t t = (int32_t)(int16_t)(raw &amp;gt;&amp;gt; 16) &amp;gt;&amp;gt; 2;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and for the Cold junction&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int32_t c = (int32_t)(int16_t)(raw &amp;amp; 0xFFFF) &amp;gt;&amp;gt; 4;&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrfb874a1"&gt;The Full Driver&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;here is the driver&amp;#39;s&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/**
 * @file    max31855.h
 * @brief   Driver for the Maxim MAX31855 SPI thermocouple amplifier.
 *
 * SolarSense populates three MAX31855KASA+ (K-type) on SPI1:
 *   
 * The device is read-only; no configuration registers exist. One 32-bit
 * SPI frame (two 16-bit HAL transactions) yields the thermocouple temp
 * (0.25 &amp;#176;C/LSB) and the on-chip cold-junction temp (0.0625 &amp;#176;C/LSB), plus
 * three open/short fault bits.
 */
#ifndef MAX31855_H
#define MAX31855_H

#include &amp;quot;stm32l4xx_hal.h&amp;quot;

/* Fault bits returned in the faults byte (mirrors hardware bits [2:0]). */
#define MAX31855_FAULT_OC   0x01U  /* thermocouple open circuit   */
#define MAX31855_FAULT_SCG  0x02U  /* thermocouple shorted to GND */
#define MAX31855_FAULT_SCV  0x04U  /* thermocouple shorted to VCC */

typedef struct {
    SPI_HandleTypeDef *hspi;
    GPIO_TypeDef      *cs_port;
    uint16_t           cs_pin;
    uint8_t            present;   /* 1 after a successful Init() SPI probe */
} MAX31855_t;

/* Bind the driver to an SPI bus and a software CS pin. Performs one SPI
   read to confirm the device is wired. Sets present = 1 on success. */
HAL_StatusTypeDef MAX31855_Init(MAX31855_t *dev, SPI_HandleTypeDef *hspi,
                                GPIO_TypeDef *cs_port, uint16_t cs_pin);

/* Read thermocouple and cold-junction temperatures (tenths of &amp;#176;C, signed)
   and the fault byte. Any output pointer may be NULL.
   Returns HAL_OK on a clean read; HAL_ERROR if the SPI transaction fails.
   A non-zero *faults value means the thermocouple has a wiring fault and
   *tc10 is unreliable, but *cj10 (board ambient) remains valid. */
HAL_StatusTypeDef MAX31855_Read(MAX31855_t *dev,
                                int32_t *tc10, int32_t *cj10,
                                uint8_t *faults);

#endif /* MAX31855_H */&lt;/pre&gt;```&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/**
 * @file    max31855.c
 * @brief   Driver for the Maxim MAX31855 SPI thermocouple amplifier.
 */
#include &amp;quot;max31855.h&amp;quot;

#define SPI_TIMEOUT_MS 10U

/* Pull CS low, clock out 32 bits as two 16-bit HAL frames, release CS. */
static HAL_StatusTypeDef read_raw(MAX31855_t *dev, uint32_t *raw)
{
    uint16_t w[2];
    HAL_GPIO_WritePin(dev-&amp;gt;cs_port, dev-&amp;gt;cs_pin, GPIO_PIN_RESET);
    HAL_StatusTypeDef st = HAL_SPI_Receive(dev-&amp;gt;hspi, (uint8_t *)w, 2, SPI_TIMEOUT_MS);
    HAL_GPIO_WritePin(dev-&amp;gt;cs_port, dev-&amp;gt;cs_pin, GPIO_PIN_SET);
    if (st == HAL_OK)
    {
        /* SPI1 is MSB-first 16-bit mode, so w[0] = bits[31:16], w[1] = bits[15:0]. */
        *raw = ((uint32_t)w[0] &amp;lt;&amp;lt; 16) | w[1];
    }
    return st;
}

HAL_StatusTypeDef MAX31855_Init(MAX31855_t *dev, SPI_HandleTypeDef *hspi,
                                GPIO_TypeDef *cs_port, uint16_t cs_pin)
{
    dev-&amp;gt;hspi    = hspi;
    dev-&amp;gt;cs_port = cs_port;
    dev-&amp;gt;cs_pin  = cs_pin;
    dev-&amp;gt;present = 0;

    /* No config registers to write; probe by doing one SPI read. */
    uint32_t raw;
    if (read_raw(dev, &amp;amp;raw) != HAL_OK)
        return HAL_ERROR;

    dev-&amp;gt;present = 1;
    return HAL_OK;
}

HAL_StatusTypeDef MAX31855_Read(MAX31855_t *dev,
                                int32_t *tc10, int32_t *cj10,
                                uint8_t *faults)
{
    uint32_t raw;
    HAL_StatusTypeDef st = read_raw(dev, &amp;amp;raw);
    if (st != HAL_OK)
        return st;

    if (faults)
        *faults = (uint8_t)(raw &amp;amp; 0x07U);

    /* Thermocouple temp: bits[31:18], 14-bit two&amp;#39;s complement, 0.25 &amp;#176;C/LSB.
       Cast top half to int16_t (sign bit preserved), arithmetic-right-shift
       by 2 to drop the reserved bit and FAULT bit.
       Tenths: raw14 &amp;#215; 0.25 &amp;#215; 10 = raw14 &amp;#215; 5 / 2. */
    if (tc10)
    {
        int32_t t = (int32_t)(int16_t)(raw &amp;gt;&amp;gt; 16) &amp;gt;&amp;gt; 2;
        *tc10 = (t * 5) / 2;
    }

    /* Cold-junction temp: bits[15:4], 12-bit two&amp;#39;s complement, 0.0625 &amp;#176;C/LSB.
       Same trick on the bottom half; shift right 4 drops reserved + fault bits.
       Tenths: raw12 &amp;#215; 0.0625 &amp;#215; 10 = raw12 &amp;#215; 10 / 16. */
    if (cj10)
    {
        int32_t c = (int32_t)(int16_t)(raw &amp;amp; 0xFFFFU) &amp;gt;&amp;gt; 4;
        *cj10 = (c * 10) / 16;
    }

    return HAL_OK;
}&lt;/pre&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;this plus a lot of Serial print commands, Get the output&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_19_5F00_10_2D00_52_2D00_53.png" /&gt;&lt;/p&gt;
&lt;p&gt;Now, the serial port is going to output a lots of data because I just put in the template for all sensors so that i can test it out with the LabView. But the tc3_c and tc3_cj are the numbers that matter. But it was not right, It showed 305 and 308 which means it is 3.05 and 3.08deg respectively (look at the top few lines of the serial). I simply multiplied the value in the code by 10 to get 3070 and 3080 respectively.There was nothing logical i know or found to make this multiplication, but it simply made sense to do it. I tried holding the junction with my hand and the temperature rose accordingly.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrffub1f0"&gt;How Accurate am I reading it?&lt;/h2&gt;
&lt;p&gt;This is going to be a difficult one to answer because I do not have a True Reference or A Calibrated device to measure and check.&amp;nbsp;But for now, I tired reading at the tip of my TS101 soldering station set at 210 deg C, I got 185 degC. Now, the Tip might not be exactly 210deg depending on where the control sensor of the soldering iron is, but I&amp;nbsp;am gonna see if calibrating it it by a combination of Boiling water and Ice will be helpful. That i will document in the last post.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:370px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x740/__key/communityserver-discussions-components-files/453/20260619_5F00_134154.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jrf8a3jq6"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;This being a stretch goal, I completed it before even completing my primary goals just because it was easy and convenient. But at this time of writing the post I have completed all of them and I am collecting data from the final device. More posts to come.&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Measuring temperature using Omega K-Type Thermocouple</title><link>https://community.element14.com/thread/57038?ContentTypeID=0</link><pubDate>Thu, 18 Jun 2026 06:22:25 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:09dfe464-b9c5-4e3a-bf39-6e572875893d</guid><dc:creator>taifur</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57038?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57038/measuring-temperature-using-omega-k-type-thermocouple/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;For my project, the measurement of a machine temperature is required. A thermocouple is one of the best sensors for measuring the temperature of an electrical machine. Thanks to Omega for sending several K-type thermocouples as a part of the Challenger kit. These thermocouples are capable of measuring from -200&amp;deg;C to 1250&amp;deg;C. For measuring temperature using any microcontroller, we need to use an amplifier and signal conditioning circuit like MAX31855 or MAX6675. As the thermocouples came without any amplifier module, I managed a MAX6675 module to connect the thermocouple with the ESP32. The following&amp;nbsp;image shows my connection of the thermocouple with the amplifier module.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="max-height:605px;max-width:807px;"  height="605" src="https://community.element14.com/resized-image/__size/1614x1210/__key/communityserver-discussions-components-files/453/IMG_5F00_3926.JPG" width="807" /&gt;&lt;/p&gt;
&lt;p&gt;Then I connected the ESP32 module with the amplifier module as shown in the following image.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;&lt;img loading="lazy" alt="image" style="max-height:604px;max-width:806px;"  height="604" src="https://community.element14.com/resized-image/__size/1612x1208/__key/communityserver-discussions-components-files/453/IMG_5F00_3921-_2800_1_2900_.JPG" width="806" /&gt;&lt;/p&gt;
&lt;p&gt;ESP32 module was programmed using Arduino IDE. I installed the MAX6675 Arduino library.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/max66751.png" /&gt;&lt;/p&gt;
&lt;p&gt;Then uploaded the following test code to the ESP32 module:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#include &amp;quot;max6675.h&amp;quot;

int thermoDO = 19;
int thermoCS = 23;
int thermoCLK = 5;

MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

void setup() {
  Serial.begin(9600);

  Serial.println(&amp;quot;MAX6675 test&amp;quot;);
  // wait for MAX chip to stabilize
  delay(500);
}

void loop() {
  // basic readout test, just print the current temp
  
  Serial.print(&amp;quot;C = &amp;quot;); 
  Serial.println(thermocouple.readCelsius());
  Serial.print(&amp;quot;F = &amp;quot;);
  Serial.println(thermocouple.readFahrenheit());
  
  // For the MAX6675 to update, you must delay AT LEAST 250ms between reads!
  delay(1000);
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;At room temperature, without connecting anything, the thermocouple, I got the following response in the serial terminal:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="max-height:413px;max-width:463px;"  height="413" src="https://community.element14.com/resized-image/__size/926x826/__key/communityserver-discussions-components-files/453/tmp23.png" width="463" /&gt;&lt;/p&gt;
&lt;p&gt;I connected my hot soldering iron tip to the thermocouple and got the following data.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="max-height:564px;max-width:752px;"  height="564" src="https://community.element14.com/resized-image/__size/1504x1128/__key/communityserver-discussions-components-files/453/IMG_5F00_3924.JPG" width="752" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="max-height:374px;max-width:421px;"  height="374" src="https://community.element14.com/resized-image/__size/842x748/__key/communityserver-discussions-components-files/453/maxtemp.png" width="421" /&gt;&lt;/p&gt;
&lt;p&gt;I noticed the temperature has increased, but I am not sure whether this is the correct temperature or if I need calibration. I will try to find it later.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>4 Communication rocks!!!</title><link>https://community.element14.com/thread/57033?ContentTypeID=0</link><pubDate>Tue, 16 Jun 2026 01:58:24 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:01b5934b-0359-4352-810f-6a440b09c850</guid><dc:creator>pandoramc</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/57033?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57033/4-communication-rocks/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;A computer system is not able to process an analog signal. Despite this fact, a digital system, such as a processor, can process an enormous amount of data to make decisions about physical variables. How is a physical phenomenon described by the data stored in a memory section? The answer is simple but quite difficult to implement. An analog-to-digital converter is required to take an analog signal and convert it to digital data. This process involves a time-based sampling to observe the phenomenon, quantify the observed value and assign a digital code that is representative for the digital system.&lt;/p&gt;
&lt;p&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt="Analog-to-Digital convertion" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/ADC.png" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 1 Analog-to-Digital convertion representation&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;One of the most important components in signal processing, whether analog or digital, is the signal conditioner. A signal conditioner is part of the sensing system since it converts a phenomenon into a voltage response. There are many types of temperature transducers that detect changes in the environment such as RTDs and NTCs. This time, thanks to the On the Line Design Challenge, there are K-type thermocouples. In my last blog I wrote about the probe mounting for harsh environments and fluid isolation. However, a question arose: How can I connect the probe to store data in the processor memory when the sensing stage provides some mV/&amp;deg;C and the dynamic range of the ADCs is huge compared with the thermocouple response? An example considered is for a 1V1 ADC_REF (silicon gap) for a 10-bit resolution ADC. In this case, the minimum detectable value is around 1.07 mV, but the ADC_REF must be small to detect small changes in temperature. This is not feasible for the measurement system using the default references on the board for each channel required.&lt;/p&gt;
&lt;h1 id="mcetoc_1jr71kh0a1"&gt;Methodology&lt;/h1&gt;
&lt;p style="text-align:left;"&gt;After exploring multiple options for creating an interface between the thermocouple and the data storage, I decided to use a commercial option to acquire temperature data. The first option I considered was using the DeviceNet protocol with an Allen Bradley PLC. Although the 1769-32E CPU is not recommended for new designs, it can perform interesting tasks. At least four thermocouples can be connected using the 1769 Thermocouple module, but the connection of Cold Junction Compensation (CJC) is required for the correct measurement of the environment. On the other hand, special thermoblocks and wire extensions are required for the connection with the module since a small wiring variation can induce offset and/or non-linearities in the measurement. Another option is to use temperature transmitters or analog front ends (AFE). While these elements have the same inconvenience of the thermocouple module for wiring, most of the AFE found has a temperature range above 0 &amp;deg;C. For test purposes are useful, but in the future the system requires measurement below this magnitude. The last option is the UT325F of UNI-T thermometer system. This equipment has up to 4 channels in multiple kinds of probes; it is possible to adjust the temperature offset since the CJC is internal and it has a Windows/Android application to capture data.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}Interfaces&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="PLC assembly" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/PLC.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Thermal blocks" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Themoblocks.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="UT325F assembly" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/UNIT.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 2 Data acquisition assembly options for thermocouple connection&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you wonder, I asked myself a question, is it possible to get data in the Arduino UNO Q Linux system? The answer, YES!!!&lt;/p&gt;
&lt;p&gt;I did a lot of tests capturing the data and recognizing patterns in the received frame. The procedure consisted of the connecting and disconnecting the probes in each channel of the thermometer. I recognized some number patterns heating the probe with my hand, but I was not able to understand it at all. Aided by an AI tool I recognized two important patterns the data is transmitted in little endian while the CRC is transmitted in big endian. The data is repetitive, but I am not sure why at the moment to write this blog. And the third pattern recognized completely by me is to inform if a probe is connected to a specific channel. This task was in a PC since the equipment has a CH340 USB-to-UART variant which Arduino UNO Q Linux image has not a driver to handle communication. This was solved compiling and installing a driver available here: &lt;a href="https://learn.sparkfun.com/tutorials/how-to-install-ch340-drivers/linux" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://learn.sparkfun.com/tutorials/how-to-install-ch340-drivers/linux&lt;/a&gt;&lt;/p&gt;
&lt;p style="text-align:left;"&gt;And the results were incredible&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="scp command" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/SCP.png" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 3 Project transfer from the Arduino UNO Q to the PC by ssh copy&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Data capture" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Capture.png" /&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 4 Serial port frame capture with data decoding&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;&lt;pre class="ui-code" data-mode="python"&gt;import serial
import struct
import time
from datetime import datetime

SERIAL_PORT = &amp;#39;/dev/ttyCH341USB0&amp;#39;
BAUD_RATE = 115200
TIMEOUT = 1
FRAME_LEN = 56

def compute_checksum(data):
	&amp;quot;&amp;quot;&amp;quot;Calculate big-endian 16-bit checksum&amp;quot;&amp;quot;&amp;quot;
	total = sum(data) &amp;amp; 0xFFFF					# 16-bits CRC
	return total.to_bytes(2, &amp;#39;big&amp;#39;)

def parse_temperature(frame):
	&amp;quot;&amp;quot;&amp;quot;
	Extract 4 Float numbers in little endian format
	Initial index is equal to 5
	&amp;quot;&amp;quot;&amp;quot;
	start = 5
	floats = []
	for i in range(4):
		b = frame[start + i*4 : start + i*4 + 4]
		if len(b) == 4:
			val = struct.unpack(&amp;#39;&amp;lt;f&amp;#39;, b)[0]
			floats.append(val)
		else:
			floats.append(None)
	return floats

def parse_connected(frame):
	&amp;quot;&amp;quot;&amp;quot;
	Verify that a Thermocouple N has a connected sensor
	Each bytes illustrates that the channel has a connected thermocouple if
	the code is 0x00, or 0x30 if not
	&amp;quot;&amp;quot;&amp;quot;
	start = 21
	channels = []
	for i in range(4):
		v = frame[21 + i]
		if v == 0x00:
			channels.append(True)
		else:
			channels.append(False)
	return channels

def read_and_validate(ser):
    &amp;quot;&amp;quot;&amp;quot;Serial port data reading. If the received frame is valid then return it&amp;quot;&amp;quot;&amp;quot;
    buffer = bytearray()
    while True:
        byte = ser.read(1)
        if not byte:
            continue
        buffer.append(byte[0])

        if len(buffer) &amp;gt;= 2 and buffer[-2] == 0xAA and buffer[-1] == 0x55:
            while len(buffer) &amp;lt; FRAME_LEN:
                b = ser.read(1)
                if not b:
                    break
                buffer.append(b[0])
            if len(buffer) &amp;gt;= FRAME_LEN:
                frame = buffer[-FRAME_LEN:]
                data_part = frame[:-2]
                recv_checksum = frame[-2:]
                calc_checksum = compute_checksum(data_part)
                if recv_checksum == calc_checksum:
                    return frame
                else:
                    print(&amp;quot;Wrong checksum&amp;quot;)
                    buffer.pop(0)
            else:
                buffer.clear()
        else:
            if len(buffer) &amp;gt; FRAME_LEN * 2:
                buffer = buffer[-FRAME_LEN:]


def main():
	try:
		ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=TIMEOUT)
		dFile = open(&amp;quot;data.csv&amp;quot;, &amp;quot;w&amp;quot;)
		dFile.write(&amp;quot;t,T1,T2,T3,T4\n&amp;quot;)
		while True:
			ser.reset_input_buffer()
			frame = read_and_validate(ser)
			if frame:
				print(&amp;quot;Received Frame [hex]:&amp;quot;, &amp;#39; &amp;#39;.join(f&amp;#39;{b:02x}&amp;#39; for b in frame))
				print(&amp;quot;╔═════════════════════════╗&amp;quot;)
				temps = parse_temperature(frame)
				valid = parse_connected(frame)
				dFile.write(f&amp;quot;{datetime.now().strftime(&amp;#39;%Y-%m-%d %H:%M:%S&amp;#39;)},&amp;quot;)
				for i in range(0, len(temps)):
					if(valid[i] == True):
						print(f&amp;quot;║ Temperature {i + 1}: {temps[i]: 5.1f} &amp;#176;C ║&amp;quot;)
						dFile.write(f&amp;quot;{temps[i]},&amp;quot;)
					else:
						print(f&amp;quot;║ Temperature {i + 1}:  NaN  &amp;#176;C ║&amp;quot;)
						dFile.write(f&amp;quot;NaN,&amp;quot;)
				print(&amp;quot;╚═════════════════════════╝&amp;quot;, end=&amp;quot;\033[F\033[F\033[F\033[F\033[F\033[F&amp;quot;)
				dFile.write(&amp;quot;\b\n&amp;quot;);
				time.sleep(1)
	except serial.SerialException as e:
		print(f&amp;quot;Serial por error: {e}&amp;quot;)
	except KeyboardInterrupt:
		print(&amp;quot;\nExecution stopped by the user&amp;quot;)
	finally:
		if &amp;#39;ser&amp;#39; in locals() and ser.is_open:
			ser.close()
		if &amp;#39;dFile&amp;#39; in locals() and not dFile.closed:
			dFile.close()

if __name__ == &amp;quot;__main__&amp;quot;:
	main()
&lt;/pre&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jr71lhks2"&gt;The future&lt;/h1&gt;
&lt;p style="text-align:left;"&gt;Now, it is time to integrate the RCP interface for the usage of the microcontroller to activate the compressor. Actually, the implemented script can receive data, process and write a CSV file to be stored in memory for future consult and analysis, but it is not able still to make decisions. The last test will consist of data sharing between the processor and microcontroller to test the system threshold and hysteresis for a full-operation test.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}achievements&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="stimuli" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260611_5F00_204302.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Data stored" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/graph.png" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 5 Actual temperature data storing achievement&lt;/em&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;script&gt;window.top.e14.func.queueScripts.add(function() { window.top.e14.func.e14DynaloadGallery(window.document);}, true );&lt;/script&gt;</description></item><item><title>SolarSense - Part 5 - Making an Live Dashboard using Lab View</title><link>https://community.element14.com/thread/57030?ContentTypeID=0</link><pubDate>Sat, 13 Jun 2026 08:57:22 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:960beefa-68bb-4139-a93a-3216ac8df38e</guid><dc:creator>arvindsa</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57030?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57030/solarsense---part-5---making-an-live-dashboard-using-lab-view/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqbvjvms0"&gt;Recap:&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master&amp;#39;s thesis of a minute Shape memory Alloy based solar panel cleaning robot&lt;/p&gt;
&lt;p&gt;Previous posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;SolarSense - Part 3 - PCB Schematics Walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation" data-e14adj="t"&gt;SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1jr00nr660"&gt;Hello Lab View, Haven&amp;#39;t seen you since my Undergrad&lt;/h2&gt;
&lt;p&gt;The last time I used Lab View was during my Undergraduate academics in Control Lab, I never was a fan of high level graphical style programming plus all of my work never required Lab View, so I was happy until this challenge came up. On the bright side, I am happy to say that there is Community version available. I don&amp;#39;t remember seeing the community when I used it. Without any research of what is the difference between Community vs Professional was, I went to the community version. Obviously to activate the Community version, I had to create an account. It was No mess, No Fuss&amp;nbsp; Installation.&lt;/p&gt;
&lt;p&gt;In this post, the aim will be to create a dashboard to show values published by the Arduino Q via TCP packets. These will include, the Solar panel outputs, The Environmental parameters such as UV, AQI, Humidity and then the system parameters such as battery, Error flags etc.&amp;nbsp;&lt;/p&gt;
&lt;h2 id="mcetoc_1jr019p9r1"&gt;Getting A Gauge up and Running&lt;/h2&gt;
&lt;h3 id="mcetoc_1jr01gq9n2"&gt;The Python Code&lt;/h3&gt;
&lt;p&gt;I made a quick code to keep sending values 4 times a second. Apologies for no comments.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="python"&gt;import math
import socket
import threading
import time

HOST = &amp;quot;0.0.0.0&amp;quot;
PORT = 9000
UPDATE_INTERVAL = 0.25


def read_value(t: float) -&amp;gt; float:
    return round(50 + 45 * math.sin(t / 3.0), 2)


def handle_client(conn: socket.socket, addr) -&amp;gt; None:
    print(f&amp;quot;[+] client connected: {addr}&amp;quot;)
    start = time.monotonic()
    try:
        while True:
            value = read_value(time.monotonic() - start)
            conn.sendall(f&amp;quot;{value}\r\n&amp;quot;.encode(&amp;quot;ascii&amp;quot;))
            time.sleep(UPDATE_INTERVAL)
    except (ConnectionResetError, BrokenPipeError, OSError):
        pass
    finally:
        conn.close()
        print(f&amp;quot;[-] client disconnected: {addr}&amp;quot;)


def main() -&amp;gt; None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((HOST, PORT))
        server.listen()
        print(f&amp;quot;Gauge TCP server listening on {HOST}:{PORT}  (Ctrl+C to stop)&amp;quot;)
        try:
            while True:
                conn, addr = server.accept()
                threading.Thread(
                    target=handle_client, args=(conn, addr), daemon=True
                ).start()
        except KeyboardInterrupt:
            print(&amp;quot;\nShutting down.&amp;quot;)


if __name__ == &amp;quot;__main__&amp;quot;:
    main()&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jr01hgvu3"&gt;The Front Panel of Lab View&lt;/h3&gt;
&lt;p&gt;A Plain Gauge with range 0-100 and a Stop button&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x720/__key/communityserver-discussions-components-files/453/01_2D00_simple-guage-vi.png" /&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jr01ituo4"&gt;Block Diagram&lt;/h3&gt;
&lt;p&gt;When Creating a VI, it opens two window, one is the UI Editor and the other is the Block Diagram Editor. The Block diagram is where is the logic is executed.&amp;nbsp; I Referred to the YouTube video&amp;nbsp;&lt;a href="https://www.youtube.com/watch?v=LMtdLqmRnVU" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://www.youtube.com/watch?v=LMtdLqmRnVU&lt;/a&gt;&amp;nbsp;to make the TCP Communication logic. One thing to note is that for all Inputs and Outputs to work in a loop, it has to be in the loop, else only the value of input at the start of first iteration of the Loop will be used. Any changes or presses to the Buttons will not be considered.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x720/__key/communityserver-discussions-components-files/453/01_2D00_simple-guage-model.PNG" /&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jr01t9op5"&gt;The Result&lt;/h3&gt;
&lt;p&gt;10mins into Lab View, I have a live dashboard.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/LabVIEW_5F00_ec4sASaV1k.gif" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jr0203ak6"&gt;Building the Actual Panel&lt;/h2&gt;
&lt;p&gt;To get a full picture of the situation I need to have the following data&lt;/p&gt;
&lt;figure class="table-figure"&gt;
&lt;table border="1"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Unit&lt;/th&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;panel_efficiency[0]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0&amp;ndash;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;panel_efficiency[1]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0&amp;ndash;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;panel_efficiency[2]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0&amp;ndash;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;soiling_ratio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;5.0&amp;ndash;29.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;battery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0&amp;ndash;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tank_level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;0&amp;ndash;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;humidity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;10&amp;ndash;70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;temperature&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;&amp;deg;C&lt;/td&gt;
&lt;td&gt;13&amp;ndash;37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;uv&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DBL&lt;/td&gt;
&lt;td&gt;Index&lt;/td&gt;
&lt;td&gt;2.0&amp;ndash;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;aqi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INT&lt;/td&gt;
&lt;td&gt;&amp;micro;g/m&amp;sup3;&lt;/td&gt;
&lt;td&gt;20&amp;ndash;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;status_ok&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INT&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;error&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INT&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/figure&gt;
&lt;p&gt;These data will be sent as a JSON over TCP. For testing, i will be randomizing the values such that they oscillate between minimum and maximum.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="python"&gt;{
  &amp;quot;panel_efficiency&amp;quot;: [25.5, 24.8, 22.3],
  &amp;quot;soiling_ratio&amp;quot;: 15.2,
  &amp;quot;humidity&amp;quot;: 65.0,
  &amp;quot;temperature&amp;quot;: 28.5,
  &amp;quot;uv&amp;quot;: 6.2,
  &amp;quot;aqi&amp;quot;: 55,
  &amp;quot;battery&amp;quot;: 78.4,
  &amp;quot;status_ok&amp;quot;: 1,
  &amp;quot;error&amp;quot;: 0,
  &amp;quot;tank_level&amp;quot;: 78.4
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jr027egi7"&gt;The Front Panel&lt;/h3&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:500px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1000/__key/communityserver-discussions-components-files/453/02_2D00_front-panel-screenshot.png" /&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jr02943m8"&gt;The Block Diagram&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=kx1E5QnnLy0" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;www.youtube.com/watch&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is where I started running into trouble. I initially thought the Unflatten from JSON block would simply split the JSON into key&amp;ndash;value pairs, but it wasn&amp;rsquo;t that straightforward. I had to declare the structure of the expected JSON format using a cluster definition&amp;mdash;the long vertical box to the left of the while loop. I was able to get this working by referring to a YouTube video &lt;a href="https://www.youtube.com/watch?v=kx1E5QnnLy0" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://www.youtube.com/watch?v=kx1E5QnnLy0&lt;/a&gt;. However, I ran into another issue when declaring a Boolean. For some reason, I couldn&amp;rsquo;t find the Boolean type. To save time, I modified the Python code to send the status flags as 0/1 integers instead of Booleans.&lt;br /&gt;&lt;br /&gt;I assumed I could use an integer-to-Boolean conversion block while reading the JSON from the server, but I couldn&amp;rsquo;t find such a block either. As a workaround, I ended up using the &amp;ldquo;If Not Equal&amp;rdquo; comparison block instead. I also noticed that the UI started being slow. Not sure why.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://community.element14.com/cfs-file/__key/communityserver-discussions-components-files/453/LabVIEW_5F00_DxuZ83tJQ9.mp4"&gt;community.element14.com/.../LabVIEW_5F00_DxuZ83tJQ9.mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:500px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1000/__key/communityserver-discussions-components-files/453/02_2D00_block-diagram.png" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jr02pjfb9"&gt;The Final Working&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:480px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x960/__key/communityserver-discussions-components-files/453/02_2D00_front-panel-working.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I have not been able to get the XY Graph to work yet, But to me, It&amp;#39;s a stretch goal which i added impromptu Hopefully I should get it working by the deadline.&lt;/p&gt;
&lt;h2 id="mcetoc_1jr02saqca"&gt;Final Notes.&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;I had some time reminiscing my college days using Lab View, fighting with my classmates to claim the limited number of floating license of Lab VIew, Coordinating with my friends so that They will claim the license the second I surrender mine back into the common pool,&amp;nbsp; Getting the dashboard to work was probably the easiest part, but I still had fun. The PCB for the data collection has completed fabrication and is on the way, it should be with me in a day or two, I am still using the old POC to keep the data collection alive till then.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Smart Ventilation Motor Monitoring System #5 Getting Started with CAN Protocol on XMC4200 Platform2GO in Arduino</title><link>https://community.element14.com/thread/57029?ContentTypeID=0</link><pubDate>Fri, 12 Jun 2026 03:21:40 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:9273805b-0056-45ca-908d-7d2202079e38</guid><dc:creator>fyaocn</dc:creator><slash:comments>1</slash:comments><comments>https://community.element14.com/thread/57029?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57029/smart-ventilation-motor-monitoring-system-5-getting-started-with-can-protocol-on-xmc4200-platform2go-in-arduino/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqst06qb0"&gt;1 Getting start with CAN&lt;/h2&gt;
&lt;p&gt;For test CAN in the other end,&amp;nbsp;XMC4200 platform2go is used&lt;/p&gt;
&lt;div&gt;CAN (Controller Area Network) is for industrial and automotive embedded communication. It&amp;rsquo;s a robust, multi-master serial protocol designed for reliable real-time data exchange between microcontrollers, sensors, and actuators&amp;mdash;even in electrically noisy environments. &lt;strong&gt;Infineon XMC4200 Platform2GO&lt;/strong&gt;&amp;nbsp;with the Arduino IDE can give a quick glance.&lt;/div&gt;
&lt;p&gt;&lt;img alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/02.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqst06qc1"&gt;2 CAN in a glance&lt;/h2&gt;
&lt;div&gt;CAN was developed by Bosch in the 1980s for automotive applications, but it&amp;rsquo;s now used widely in industrial automation, robotics, and aerospace. Key features,&lt;/div&gt;
&lt;div&gt;No single &amp;ldquo;master&amp;rdquo; device; any node can send data when the bus is free.&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Error resilience&lt;/strong&gt;: Built-in error checking, arbitration, and retransmission to ensure data integrity.&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Two message formats:&amp;nbsp;&lt;/strong&gt;&lt;strong&gt;Standard CAN (11-bit ID)&lt;/strong&gt;: Shorter, faster identifiers for simple systems and&amp;nbsp;&lt;strong&gt;Extended CAN (29-bit ID)&lt;/strong&gt;: Longer identifiers for complex networks with many nodes.&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Flexible data length&lt;/strong&gt;: Classic CAN supports up to 8 bytes of data per packet; newer CAN FD extends this to 64 bytes.&lt;/div&gt;
&lt;div&gt;For the XMC4200, the on-chip CAN peripheral is fully supported by Infineon&amp;rsquo;s Arduino core, making it easy to prototype CAN applications without low-level register programming.&lt;/div&gt;
&lt;div&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/03.PNG" /&gt;&lt;/div&gt;
&lt;div&gt;CANH and CANL are two wires for communication on 12V or 24V bus.&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;h2 id="mcetoc_1jqstams72"&gt;3 USING ARDUINO IDE&lt;/h2&gt;
&lt;div&gt;Using Arduino is quick start for can&lt;/div&gt;
&lt;div&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/01.PNG" /&gt;&lt;/div&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;#include &amp;lt;CAN.h&amp;gt;
void setup() {
  Serial.begin(9600);
  while (!Serial);

  Serial.println(&amp;quot;CAN Sender&amp;quot;);

  // start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println(&amp;quot;Starting CAN failed!&amp;quot;);
    while (1);
  }
}

void loop() {
  // send packet: id is 11 bits, packet can contain up to 8 bytes of data
  Serial.print(&amp;quot;Sending packet ... &amp;quot;);

  CAN.beginPacket(0x12);
  CAN.write(&amp;#39;h&amp;#39;);
  CAN.write(&amp;#39;e&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;o&amp;#39;);
  CAN.endPacket();
  Serial.println(&amp;quot;done&amp;quot;);
  delay(1000);
  CAN.beginExtendedPacket(0xFFF);
  CAN.write(&amp;#39;w&amp;#39;);
  CAN.write(&amp;#39;o&amp;#39;);
  CAN.write(&amp;#39;r&amp;#39;);
  CAN.write(&amp;#39;l&amp;#39;);
  CAN.write(&amp;#39;d&amp;#39;);
  CAN.endPacket();

  Serial.println(&amp;quot;done&amp;quot;);

  delay(1000);
}
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The code is simple , put&amp;nbsp;loop code&amp;nbsp;in the the CAN bus&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;serial.begin(9600)&lt;/code&gt;: Initializes the UART serial port at 9600 baud for debug messages.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAN.begin(500E3)&lt;/code&gt;: Initializes the CAN bus at &lt;strong&gt;500 kbps&lt;/strong&gt;, one of the most common baud rates for automotive and industrial applications. If initialization fails (e.g., hardware issue), the code prints an error and stops.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then in&amp;nbsp; loop&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CAN.beginPacket(0x12)&lt;/code&gt;: Starts a new standard CAN packet with the 11-bit identifier &lt;code&gt;0x12&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAN.write()&lt;/code&gt;: Adds data bytes to the packet. Here, we&amp;rsquo;re sending the ASCII characters &lt;code&gt;&amp;#39;h&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;e&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;l&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;l&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;o&amp;#39;&lt;/code&gt; (5 bytes total, well within the 8-byte limit).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAN.endPacket()&lt;/code&gt;: Finalizes and sends the packet over the CAN bus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then in extend&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CAN.beginExtendedPacket(0xFFFFF)&lt;/code&gt;: Starts an extended CAN packet with the 29-bit identifier &lt;code&gt;0xFFFFF&lt;/code&gt;. This is useful when you need more unique IDs for a large network of devices.&lt;/li&gt;
&lt;li&gt;The rest works the same way: we send the characters &lt;code&gt;&amp;#39;w&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;o&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;r&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;l&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;#39;d&amp;#39;&lt;/code&gt; as data bytes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqstevdm4"&gt;4&amp;nbsp; Test for CAN on XMC4200&lt;/h2&gt;
&lt;p&gt;Baud Rate Matching: All devices on the CAN bus must use the same baud rate. 500 kbps is standard for automotive, 125 kbps is common for industrial applications.&lt;/p&gt;
&lt;p&gt;Termination Resistors: Always add 120&amp;Omega; resistors at the two ends of the CAN bus to eliminate signal reflections. This resistor is soldered in this board.&lt;/p&gt;
&lt;p&gt;Common Ground: All devices on the bus must share a common ground reference for reliable communication. Although only two line is enough for communication.&lt;/p&gt;
&lt;p&gt;The test result is output in serial terminal&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/04.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;Now it is ready for the CAN bus run as expected.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 4 - CAN Protocol Deep Dive and Implementation</title><link>https://community.element14.com/thread/57025?ContentTypeID=0</link><pubDate>Wed, 10 Jun 2026 06:23:28 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:fd532d14-bba8-49cc-ac58-ef536466bc7c</guid><dc:creator>arvindsa</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57025?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57025/solarsense---part-4---can-protocol-deep-dive-and-implementation/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqo25snc0" style="text-align:left;"&gt;Recap&lt;/h2&gt;
&lt;p&gt;I&amp;#39;m building a smart solar monitoring system that uses three panels with a clean reference to eliminate weather effects and directly measure dust-induced losses in real time. One panel stays pristine as a baseline, and comparing the two under identical sky conditions gives a performance ratio that reveals soiling immediately. The goal is to use environmental sensors and edge AI to predict exactly when cleaning is needed, before efficiency drops enough to impact revenue. This beats fixed-schedule cleaning or waiting for output to degrade.Also this is complementary to my Master&amp;#39;s thesis of a minute Shape memory Alloy based solar panel cleaning robot&lt;/p&gt;
&lt;p&gt;Previous posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough" data-e14adj="t"&gt;SolarSense - Part 3 - PCB Schematics Walkthrough&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1jqo25snc1"&gt;Why CAN?&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;Because I CAN.. Alright i will stop with the Pun. Why have CAN to say if a panel is soiled or clean? The real challenge comes during the AI training phase. Since I need to experiment with different timeframes of statistical compression (avg, max etc). I need to collect massive amounts of raw sensor data from the three panels over weeks or months to train an edge AI model that can predict soiling. That data volume cannot flow over regular wireless. I am talking about 10 sensor types updating at different rates, with measurements from multiple sample points. LoRa bandwidth would be a bottleneck. WiFi demands power and gets unreliable outdoors. Both add latency and packet loss that corrupt the data stream. Coming to think of it, it is like Stock trading, you need to identify the right timewindow to calculate the indicators of performance.&lt;/p&gt;
&lt;p&gt;My plan is to eventually deploy the measurement station at a remote location on a rooftop or pole - away from the gateway for safety and unobstructed sky access. The CAN bus with shielded twisted pair can run 50+ meters reliably without the noise and dropout issues of wireless. That cable will carry all the raw telemetry at full fidelity back to the gateway for logging and later analysis.&lt;/p&gt;
&lt;p&gt;CAN also gives me a standardized, well understood protocol. Building custom serial handling for this much data would be error prone and hard to debug. CAN peripheral support is built into the STM32 hardware. The protocol itself enforces packet integrity with checksums and acknowledgments. If a frame gets corrupted, i know about it instead of silently logging garbage.&lt;/p&gt;
&lt;p&gt;Stress testing my implementation early on a controlled bus lets me validate reliability before field deployment. If the CAN implementation fails during a rainstorm in Kerala, i will lose months of data. Better to find problems in the lab.&lt;/p&gt;
&lt;p&gt;Once the model is trained and deployed remotely, the picture changes completely. At that point, the edge device runs the AI inference on all the raw sensor data locally and only sends back the results - a simple alert saying clean or soiled, confidence level, maybe a status update every hour. That traffic is tiny and LoRa is perfect for it. A few kilobytes per day over LoRa is completely practical. So the architecture splits nicely: CAN for the high bandwidth data collection phase, LoRa for the low bandwidth deployment phase.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo25snc2"&gt;The Protocol Design&lt;/h2&gt;
&lt;p&gt;The protocol uses classic CAN - standard 11-bit IDs and 8-byte payloads at 500 kbps. I avoided CAN FD because the older Nucleo boards do not support it well, and 500 kbps is more than enough for my use case.&lt;/p&gt;
&lt;p&gt;I organized the data into three traffic classes by update frequency:&lt;/p&gt;
&lt;p&gt;Fast (1 Hz): Panel voltages and currents - these change every second as clouds pass. Four frames, one for each panel and one for UV.&lt;/p&gt;
&lt;p&gt;Medium (0.2 Hz): Thermocouple and battery data. These are stable unless the environment shifts significantly.&lt;/p&gt;
&lt;p&gt;Slow (0.1 Hz): Air quality, pressure, humidity, temperature, and rain detection. These sensors are sluggish and do not need to be read faster anyway.&lt;/p&gt;
&lt;p&gt;The diagnostics channel runs at 1 Hz in both directions - time sync from the gateway to the panel, and a heartbeat from the panel back with sequence numbers and error flags. And of-course these frequency are quite aggressive, and I hope to reduce them down soon within a day or two of data collection. Aha, That gives me an idea. i should be able to set the frequency over CAN Bus. Rather than reprogram it. Why did i not think of that?&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo25snc3"&gt;The Data Structures&lt;/h2&gt;
&lt;p&gt;Looking at the code, each sensor type has its own struct. For example, a panel measurement looks like this:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;typedef struct {
    uint16_t v_mv;   // voltage in millivolts
    int32_t  i_ua;   // current in microamps
    int32_t  p_uw;   // power in microwatts (calculated at gateway)
} SS_Panel_t;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;All the fixed point scaling is documented in one place so there is no guessing. Voltage is in mV, current in microamps, temperature in degrees times 10 or times 100 depending on the sensor. This keeps the integers and avoids floating point over the bus.&lt;/p&gt;
&lt;p&gt;The frame layout is compact. A panel reading takes 6 bytes: 2 for voltage, 4 for current. The gateway computes power on arrival, so we do not waste payload space transmitting it. Total bus load at worst case is only 0.15 percent of 500 kbps capacity - we could handle backfill replays of thousands of missed frames per second if the SD card goes offline temporarily.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo26jgo4"&gt;The Sender - F103 Nucleo&lt;/h2&gt;
&lt;p&gt;The Nucleo-F103RB is the first Nucleo board I purchased some 10 years ago when I started my STM32 Journey. but it has a CAN peripheral it may be the older BX standard, lower frame size but that is all i need. The sender firmware is split into two modes.&lt;/p&gt;
&lt;p&gt;Loopback mode runs a stress test. All 10 frame types are sent as fast as the CAN peripheral can push them - roughly one frame per millisecond - and the firmware waits for each echo before sending the next. If the byte stream matches exactly, the LED stays on. If anything corrupts, it goes dark. Running this for roughly an hour gives me confidence the pack and unpack functions are symmetric. Truth be said, i stress tested because I wanted to see the limits out of pure curiosity.&lt;/p&gt;
&lt;p&gt;Normal mode transmits at the actual needed frequency. One second pulse ticks at 1 Hz and sends all four fast frames. Every five seconds the medium frames go out. Every ten seconds come the slow frames. This matches the data collectors which do not need constant reads anyway.&lt;/p&gt;
&lt;p&gt;The pack functions are straightforward - memcpy the struct fields into the eight byte buffer in little-endian order. Here is what the pack function actually does under the hood:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;void can_pack_panel(SS_Panel_t *p, uint8_t *buf)
{
    buf[0] = (p-&amp;gt;v_mv) &amp;amp; 0xFF;         // voltage low byte
    buf[1] = (p-&amp;gt;v_mv &amp;gt;&amp;gt; 8) &amp;amp; 0xFF;    // voltage high byte
    buf[2] = (p-&amp;gt;i_ua) &amp;amp; 0xFF;         // current byte 0
    buf[3] = (p-&amp;gt;i_ua &amp;gt;&amp;gt; 8) &amp;amp; 0xFF;    // current byte 1
    buf[4] = (p-&amp;gt;i_ua &amp;gt;&amp;gt; 16) &amp;amp; 0xFF;   // current byte 2
    buf[5] = (p-&amp;gt;i_ua &amp;gt;&amp;gt; 24) &amp;amp; 0xFF;   // current byte 3
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;SS_Panel_t p = { .v_mv = 5500, .i_ua = 175000 };
can_pack_panel(&amp;amp;p, tx_buf);
tx_frame(SS_ID_PANEL1, 6);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;This generates 6 bytes on the wire: voltage takes 2 bytes, current takes 4 bytes, all in little-endian byte order. So a voltage of 5500 mV becomes 0x5C 0x15 on the wire (5500 = 0x157C, but reversed for little-endian).&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:444px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x888/__key/communityserver-discussions-components-files/453/03_2D00_01.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo28k625"&gt;The Receiver - Arduino Q with STM32U585&lt;/h2&gt;
&lt;p&gt;As i covered in Part , the Arduino Q was jealous of my relation with STM32 and so he had Zephyr and arduino-router locking me out of the CAN. The solution was to take control of the STM32U585 directly using STM32HAL. Once i disabled the arduino-router service, i could flash bare metal firmware. Cut out the middle man I say.&lt;/p&gt;
&lt;p&gt;The receiver listens passively on the bus. When a frame arrives, it checks the ID and dispatches to an unpack function. The unpack function mirrors the pack logic - it extracts the bytes in little-endian order and reconstructs the struct:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;void can_unpack_panel(uint8_t *buf, SS_Panel_t *p)
{
    // voltage from bytes 0-1
    p-&amp;gt;v_mv = buf[0] | (buf[1] &amp;lt;&amp;lt; 8);        
    // current from bytes 2-5
    p-&amp;gt;i_ua = buf[2] | (buf[3] &amp;lt;&amp;lt; 8) | (buf[4] &amp;lt;&amp;lt; 16) | (buf[5] &amp;lt;&amp;lt; 24); 
    // compute power
    p-&amp;gt;p_uw = (p-&amp;gt;v_mv * p-&amp;gt;i_ua) / 1000;  
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;When 6 bytes arrive from the CAN bus, the unpack function reassembles them back into the struct. So the bytes 0x5C 0x15 become 5500 mV again, and 0xF8 0xAB 0x02 0x00 becomes 175000 microamps. Then it computes power on the fly by multiplying voltage times current and dividing by 1000.&lt;/p&gt;
&lt;p&gt;The dispatch loop in the receiver routes each frame type to its unpack function:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;switch (id) {
case SS_ID_PANEL1: {
  SS_Panel_t p; 
  can_unpack_panel(rxData, &amp;amp;p);
  printf(&amp;quot;[%06lu] RX 0x101 P1_REF  V=%umV  I=%ld&amp;#181;A  P=%ld&amp;#181;W\r\n&amp;quot;,
         tick, p.v_mv, p.i_ua, p.p_uw);
  break;
}
case SS_ID_PANEL2: {
  SS_Panel_t p;
  can_unpack_panel(rxData, &amp;amp;p);
  printf(&amp;quot;[%06lu] RX 0x102 P2_SOIL V=%umV  I=%ld&amp;#181;A  P=%ld&amp;#181;W\r\n&amp;quot;,
         tick, p.v_mv, p.i_ua, p.p_uw);
  break;
}
case SS_ID_THERMO: {
  SS_Thermo_t t;
  can_unpack_thermo(rxData, &amp;amp;t);
  printf(&amp;quot;[%06lu] RX 0x201 TC      surface=%.1fC  cold=%.1fC\r\n&amp;quot;,
         tick, t.surface_c10 / 10.0f, t.cold_c10 / 10.0f);
  break;
}
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Each frame ID has a unique handler. When a panel frame arrives, it unpacks to a panel struct and prints the voltage, current, and computed power. When a thermocouple frame arrives, it unpacks to a thermo struct and prints temperatures. This is where the computation happens - the sender does not transmit power at all.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo2dlrn6"&gt;Transceiver Hardware&lt;/h2&gt;
&lt;p&gt;The F103 Nucleo uses the Waveshare RS485/CAN Shield which sits on top and breaks out the CAN TX and RX lines. This shield has an onboard 120 ohm termination resistor already built in.&lt;/p&gt;
&lt;p&gt;The Arduino Q runs the MAX33041EVAL shield which also has CAN transceiver circuitry. When i first connected the two boards, nothing worked. Frames were not getting through at all.&lt;/p&gt;
&lt;p&gt;I added a common ground wire between the two boards since CAN is a differential bus and needs a shared reference. That did not fix the communication, but i kept it&amp;nbsp; anyway.&lt;/p&gt;
&lt;p&gt;The real issue was the termination resistors. I pulled out the multimeter and checked. The Waveshare board measured 120 ohms. The MAX33041EVAL measured 60 ohms.&amp;nbsp; Google Search AI said &amp;quot;Having different termination values on the same bus creates an impedance mismatch - the signal reflections get mangled and frames corrupt or disappear entirely.&amp;quot;&lt;/p&gt;
&lt;p&gt;The MAX33041EVAL has jumpers that can change its termination. I adjusted them to disable the 60 ohm termination and enable 120 ohm instead, matching the Waveshare side. Once both sides had 120 ohm, communication became rock solid immediately.&lt;/p&gt;
&lt;p&gt;Here are the multimeter readings that showed the problem and the fix:&lt;/p&gt;
&lt;h4&gt;On the Waveshare CAN Shield&lt;/h4&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/03_2D00_02.jpg" /&gt;&lt;/p&gt;
&lt;h4&gt;On MAX33401EVAL Board with default Jumpers&lt;/h4&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:500px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1000/__key/communityserver-discussions-components-files/453/03_2D00_03.jpg" /&gt;&lt;/p&gt;
&lt;h4&gt;On MAX33401EVAL Board with changed Jumpers&lt;/h4&gt;
&lt;p&gt;Jumper Details as per:&amp;nbsp;&lt;a id="" href="https://www.analog.com/media/en/technical-documentation/data-sheets/max33041eshld.pdf" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://www.analog.com/media/en/technical-documentation/data-sheets/max33041eshld.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The star mark refers to the default position of jumpers when shipped. So I had to just change the position of the JU7 and JU8&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/pastedimage1781072397735v1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/03_2D00_04.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo2efm57"&gt;Putting It Together&lt;/h2&gt;
&lt;p&gt;To test the whole system&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/03_2D00_05.jpg" /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mount the Waveshare RS485/CAN Shield on the F103 Nucleo&lt;/li&gt;
&lt;li&gt;Mount the MAX33041EVAL shield on the Arduino Q&lt;/li&gt;
&lt;li&gt;Configure the MAX33041EVAL jumpers to disable termination (check the datasheet for which jumpers)&lt;/li&gt;
&lt;li&gt;Wire CAN_H and CAN_L from the Waveshare board to the MAX33041EVAL board via shielded twisted pair&lt;/li&gt;
&lt;li&gt;Compile and flash both boards&lt;/li&gt;
&lt;li&gt;Open two serial terminals one directly, and the other via SSH terminal of ArduinoQ. Still using the same python script as in my Part 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The F103 prints what it is sending. The Arduino Q prints what it is receiving. If both match line by line, the protocol is working. If frames are getting dropped or corrupted, check that the jumpers on the MAX33041EVAL are set correctly.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://community.element14.com/cfs-file/__key/communityserver-discussions-components-files/453/2026_2D00_06_2D00_09-20_2D00_11_2D00_40.mp4"&gt;community.element14.com/.../2026_2D00_06_2D00_09-20_2D00_11_2D00_40.mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is a video captured using another App i am experimenting OBS studio. It did allow me to capture just the windows i am interested in without showing others.&amp;nbsp;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo2gd6j8"&gt;What Comes Next&lt;/h2&gt;
&lt;p&gt;The protocol and firmware are solid. The next step is to integrate the actual sensor readings - right now i am transmitting dummy test data. The STM32L476 on the data collection PCB will read from the INA219B current sensors, the MAX31855 thermocouple interface, the BME280, SEN66, and HX94C humidity sensor, then pack them into CAN frames.&lt;/p&gt;
&lt;p&gt;Once that is working, i need to add SD card logging on the collection board as a backup, and implement the time sync heartbeat so the two boards stay in sync. Then the real fun begins - taking all that sensor data and training an edge AI model to predict when the panels need cleaning.&amp;nbsp; The infrastructure is getting solid. The CAN bus is proving to be the right choice - reliable, well understood, and not overkill for the data rates we need.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqo2h9899"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Building a custom protocol is not something to take lightly. But for a closed system like this where i control both ends, it is worth the effort. The payoff is a bus that handles exactly what i need and nothing more. The stress test gave me the confidence that the implementation works. Next post will be about actually running the sensor board and seeing real data flow across the CAN bus.&lt;/p&gt;
&lt;p&gt;There is also a problem with the Arduino Q&amp;#39;s STM32 sort of not accepting the CAN frames after few flash attempts, But the problem went out when I restarted the ArduinoQ. The problem is sporadic but I haven&amp;#39;t been find the root cause of the problem yet.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>3 The newest system setup</title><link>https://community.element14.com/thread/57023?ContentTypeID=0</link><pubDate>Tue, 09 Jun 2026 00:28:55 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:7ed83a09-f321-4546-858d-326ace03f73e</guid><dc:creator>pandoramc</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57023?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57023/3-the-newest-system-setup/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h1 id="mcetoc_1jqkr7c500"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;For the newest setup it is required to have a cooling system to do temperature testing. This is achieved by integrating of new components in order to reproduce the results of the first system here and improve the system usability. A copper coil will be used to cover the electrolyte container and mount the thermocouples. Some of the necessary materials are a compressor, a condenser, a dry filter, and so on.&amp;nbsp;Once the system is assembled, the thermocouples must be mounted to monitor the temperature. Despite the thermocouples are insulated, the bead and Chromel and Alumel in the sensing terminal are not. On the other hand, the fluids used in the process could damage the probe if it has overexposing conditions.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;img style="max-height:360px;max-width:640px;" alt="CoolingSystemMaterials" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/WhatsApp-Image-2026_2D00_04_2D00_23-at-10.32.44-AM.jpeg" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 1. Materials for the newest setup&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jqkr7tmt1"&gt;Methodology&lt;/h1&gt;
&lt;p style="text-align:left;"&gt;To avoid this, the temperature probe is assembled on a thermowell. A thermowell is an accessory for the temperature probe that isolates the sensor from the environment. Unfortunately, the sensor bead and air inside the thermowell must interact with the thermocouple measurement. To prevent this, thermal paste is added to the thermowell to displace the air and facilitate thermal coupling.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}Thermal coupling&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Thermowell" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260608_5F00_134432.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Thermocouple assembly" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260608_5F00_134212.jpg" /&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 2. Thermowell assembly for environmental insulated measurements&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align:left;"&gt;Currently, the Arduino UNO Q operates with a power consumption of about 2.5 W. Initially, we used a 13-port USB hub to connect the accessories and power adapter. However, this configuration caused the board to reboot and produce errors during continuous usage. The initial hypothesis was that excessive port usage caused a peak current or memory access violation. Consequently, a hub with fewer ports than the original was used. This time, a hub with five ports has better stability over time. The available ports on the hub are HDMI, two USB-A ports, an Ethernet connection, and a USB-C power supply connection.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}GUI captures&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Code" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/code.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Directories" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/directories.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Tree" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/tree.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 3. GUI terminal captures of past development&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;" alt="Power consumption" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/power.jpg" /&gt;&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 4. Power measurement from power supply&lt;/em&gt;&lt;/p&gt;
&lt;h1 id="mcetoc_1jqkruqfi2"&gt;The future&lt;/h1&gt;
&lt;p style="text-align:left;"&gt;This setup provides access to the board through a graphical user interface. Developers interacts with the Arduino App Lab to create new applications using Python for the Linux interface and Arduino code for the microcontroller system. Now, a new challenge has been reached, how to interact with thermocouples? Thermocouples have a low voltage response to temperature, and the usage of different wiring systems could create new cold junctions in the measurement system. To mitigate this issue, special wires are required, such as K-type thermocouple extenders and/or compatible thermoblocks to avoid interference with the measurement. Hoever, this is a quite complicated situation since the temperature transmission system only guarantees the measurement for temperatures above 0&amp;deg;C(32 &amp;deg;F). This is according to the polynomial function implemented in the conditioning blocks. For our purposes, we need to measure temperatures below the mentioned temperature. At this moment, we can transmit the temperature using an interesting setup using Omega products, but it is still being tested.&lt;/p&gt;
&lt;table style="margin-left:auto;margin-right:auto;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;{gallery}Connections&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Thermalblocks" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260521_5F00_231417.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img loading="lazy" style="max-height:360px;max-width:640px;" alt="Transmitter" src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Transmitter.jpg" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style="text-align:center;"&gt;&lt;em&gt;Figure 5. Building blocks for the temperature measurement&lt;/em&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;script&gt;window.top.e14.func.queueScripts.add(function() { window.top.e14.func.e14DynaloadGallery(window.document);}, true );&lt;/script&gt;</description></item><item><title>Forum#4: Adapting the Sentinel Vision Model for Industrial Defect Detection (ILS)</title><link>https://community.element14.com/thread/57022?ContentTypeID=0</link><pubDate>Sun, 07 Jun 2026 04:13:59 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:b4c0d81f-eee6-467a-89c2-f5f230002873</guid><dc:creator>skruglewicz</dc:creator><slash:comments>4</slash:comments><comments>https://community.element14.com/thread/57022?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57022/forum-4-adapting-the-sentinel-vision-model-for-industrial-defect-detection-ils/rss?ContentTypeId=0</wfw:commentRss><description>&lt;div class="ProseMirror" data-disable-editing="false"&gt;
&lt;h1&gt;Forum#4: Adapting the Sentinel Vision Model for Industrial Defect Detection (ILS)&lt;/h1&gt;
&lt;p&gt;Hello fellow challengers! Welcome to my 4th update.&lt;/p&gt;
&lt;p&gt;In my original application, I proposed a specific goal for my Industrial Defect Detection (ILS) system: &lt;em&gt;&amp;quot;I will deploy a quantized MobileNet-SSD model within a headless Docker container to detect safety gear (glasses/vests) and &amp;#39;Red Zone&amp;#39; incursions. To manage the 2GB RAM limit and avoid swap exhaustion, the vision pipeline will use a motion-triggered inference engine.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Today, I&amp;rsquo;m excited to share the step-by-step progress of this experiment using the &lt;strong&gt;Arduino UNO Q&lt;/strong&gt;, focusing on how to securely deploy a custom vision &amp;quot;Brick&amp;quot; while strictly managing hardware resources.&lt;/p&gt;
&lt;h2&gt;1. Hardware Setup: Using a USB Camera on the UNO Q&lt;/h2&gt;
&lt;p&gt;The new Arduino UNO Q features a fantastic dual-brain architecture. It pairs an STM32 microcontroller (for real-time hardware control) with a Qualcomm Dragonwing quad-core processor running a full Debian Linux OS.&lt;/p&gt;
&lt;p&gt;Because we have a full Linux environment, adding &amp;quot;eyes&amp;quot; to the system is incredibly straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Connection:&lt;/strong&gt; The UNO Q features a high-speed USB-C port. By using a standard USB-C hub or a USB-A to USB-C adapter, I plugged in a standard UVC (USB Video Class) webcam.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The OS Layer:&lt;/strong&gt; Linux natively recognizes the camera. It automatically mounts it to &lt;code&gt;/dev/video0&lt;/code&gt;. There is no need for complex microcontroller camera drivers; the MPU treats it exactly as a desktop computer would!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. The Strategy: Motion-Triggered Inference&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt; The UNO Q has 2GB of RAM. A continuous video feed running through a deep learning object detection model (like MobileNet-SSD) can easily consume 1.5GB+. When Linux runs out of RAM, it uses &amp;quot;swap&amp;quot; space on the eMMC storage. Constant swapping will quickly wear out the flash memory and crash the system (Swap Exhaustion).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Novice-Friendly Solution:&lt;/strong&gt; Imagine a security guard staring at an empty hallway. Instead of staring intently 100% of the time, the guard only focuses when they see movement. We will do the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The camera captures frames.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A lightweight &amp;quot;Motion Detection&amp;quot; algorithm compares the current frame to the previous one. This uses almost &lt;em&gt;zero&lt;/em&gt; RAM.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If movement is detected, it &amp;quot;wakes up&amp;quot; the heavy AI model to check for Safety Vests, Glasses, or Red Zone incursions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Data Flow Diagram&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;flowchart TD
    A[USB Camera /dev/video0] --&amp;gt; B(Capture Frame)
    B --&amp;gt; C{Is there Motion?}
    C -- No --&amp;gt; B
    C -- Yes --&amp;gt; D[Trigger AI Inference]
    D --&amp;gt; E[Quantized MobileNet-SSD]
    E --&amp;gt; F{Objects Detected?}
    F -- Safety Gear Missing --&amp;gt; G[Log Safety Violation]
    F -- Red Zone Breach --&amp;gt; H[Trigger Alarm on STM32 MCU]
    F -- All Clear --&amp;gt; B
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Creating the Custom &amp;quot;Brick&amp;quot; Model&lt;/h2&gt;
&lt;p&gt;In the Arduino App Lab ecosystem, a &amp;quot;Brick&amp;quot; is a modular block of software. We are building a &lt;strong&gt;Custom AI Vision Brick&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To make this fit in our 2GB limit, we use a technique called &lt;strong&gt;Quantization&lt;/strong&gt;. Normally, AI models use 32-bit floating-point numbers (high precision, large file size). By quantizing the MobileNet-SSD model to INT8 (8-bit integers), we shrink the model footprint from ~20MB down to ~4MB, drastically reducing the RAM required to load the model into memory while maintaining enough accuracy to detect high-contrast items like neon safety vests.&lt;/p&gt;
&lt;h2&gt;4. Step-by-Step Implementation Guide&lt;/h2&gt;
&lt;p&gt;Here is how I designed, coded, and implemented this on the UNO Q.&lt;/p&gt;
&lt;h3&gt;Step 1: Prepare the Headless Docker Environment&lt;/h3&gt;
&lt;p&gt;&amp;quot;Headless&amp;quot; simply means we are running the software in the background without a graphical desktop (no windows, no mouse pointer). This alone saves about 400MB of RAM!&lt;/p&gt;
&lt;p&gt;We write a &lt;code&gt;Dockerfile&lt;/code&gt; that installs only the bare minimum: Python, OpenCV (headless version), and our AI runtime (like TensorFlow Lite).&lt;/p&gt;
&lt;h3&gt;Step 2: The Motion-Triggered Code Logic&lt;/h3&gt;
&lt;p&gt;Here is a simplified look at the Python script running inside our Docker container. Notice how we only invoke the heavy &lt;code&gt;detect_objects&lt;/code&gt; function if the motion threshold is met.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cv2
import time

# Initialize USB Camera
cap = cv2.VideoCapture(0)
ret, frame1 = cap.read()
ret, frame2 = cap.read()

while cap.isOpened():
    # 1. Calculate absolute difference between frames for motion
    diff = cv2.absdiff(frame1, frame2)
    gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5,5), 0)
    _, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
    
    # 2. Find contours of the motion
    contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    motion_detected = False
    for contour in contours:
        if cv2.contourArea(contour) &amp;gt; 5000: # Adjust sensitivity here
            motion_detected = True
            break
            
    # 3. Trigger the Heavy AI Model ONLY if motion is found
    if motion_detected:
        print(&amp;quot;Motion detected! Triggering MobileNet-SSD...&amp;quot;)
        # --&amp;gt; Run Quantized INT8 Inference Here &amp;lt;--
        # results = run_mobilenet_ssd(frame1)
        # check_red_zone_incursion(results)
    
    # Update frames
    frame1 = frame2
    ret, frame2 = cap.read()
    
    # Small sleep to free up CPU cycles
    time.sleep(0.1) 
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Deployment with Strict RAM Limits&lt;/h3&gt;
&lt;p&gt;To absolutely ensure we never hit swap exhaustion, we deploy our container using Docker Compose with strict memory limits enforced.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# docker-compose.yml
version: &amp;#39;3.8&amp;#39;
services:
  sentinel_vision:
    build: .
    devices:
      - &amp;quot;/dev/video0:/dev/video0&amp;quot; # Passthrough the USB camera
    deploy:
      resources:
        limits:
          memory: 800M # Strictly limit the container to 800MB RAM
    restart: unless-stopped
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 4: Connecting MPU to MCU (The Output)&lt;/h3&gt;
&lt;p&gt;When the Linux side (MPU) detects a missing safety vest or a Red Zone incursion, it sends a simple serial payload to the real-time STM32 microcontroller (MCU) side of the UNO Q. The MCU then instantly flips a relay to sound an alarm or halt a machine, with zero latency!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By combining a headless Docker architecture, an INT8 quantized MobileNet-SSD model, and a motion-triggered inference gate, we completely bypass the standard bottlenecks of edge AI. We keep RAM usage under 1GB, protect the onboard eMMC from swap wear, and maintain rapid response times for industrial safety.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I would love to hear your feedback!&lt;/strong&gt; 1. Has anyone else experimented with INT8 quantization on the UNO Q?&lt;/p&gt;
&lt;p&gt;2. Are there better algorithms than &lt;code&gt;cv2.absdiff&lt;/code&gt; for lightweight motion triggering in varying industrial lighting?&lt;/p&gt;
&lt;p&gt;Let me know your thoughts below!&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Forum#5: UNO Q &amp; Grove Starter Kit V3 - Building the Safety Alert System (ILS)</title><link>https://community.element14.com/thread/57021?ContentTypeID=0</link><pubDate>Sun, 07 Jun 2026 04:10:13 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:6d805d81-6c22-4596-b673-4970a8ee6993</guid><dc:creator>skruglewicz</dc:creator><slash:comments>2</slash:comments><comments>https://community.element14.com/thread/57021?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57021/forum-5-uno-q-grove-starter-kit-v3---building-the-safety-alert-system-ils/rss?ContentTypeId=0</wfw:commentRss><description>&lt;div class="ProseMirror" data-disable-editing="false"&gt;
&lt;h2 id="mcetoc_1jqg45tag1"&gt;Forum #5: UNO Q &amp;amp; Grove Starter Kit V3&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;Hello fellow challengers&lt;/p&gt;
&lt;p&gt;Building on my Forum #4 post&amp;mdash;where I outlined using the Arduino UNO Q&amp;#39;s AI vision capabilities to detect safety gear (glasses/vests) and Red Zone incursions&amp;mdash;it&amp;rsquo;s time to bring that system into the physical world. Detection is only half the battle; we need to instantly &lt;strong&gt;alert&lt;/strong&gt; operators when a safety breach occurs.&lt;/p&gt;
&lt;p&gt;For this phase, I will be using the &lt;strong&gt;Grove Starter Kit V3&lt;/strong&gt; by connecting (piggybacking) its Base Shield onto our Arduino UNO Q. Here is my extensive, step-by-step novice-friendly guide to designing, coding, and implementing this alert system using the &lt;strong&gt;Arduino App Lab&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg45tag1"&gt;1. Hardware Integration: Piggybacking the Base Shield on the UNO Q&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What does &amp;quot;piggybacking&amp;quot; mean?&lt;/strong&gt; The Grove Base Shield V3 is an expansion board (a &amp;quot;shield&amp;quot;). The Arduino UNO Q maintains the classic UNO form factor and pin layout. &amp;quot;Piggybacking&amp;quot; simply means we align the male pins on the bottom of the Grove Base Shield with the female headers on the top of the UNO Q, and press them firmly together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why use the Base Shield?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of dealing with messy wires, resistors, and breadboards, the Base Shield gives us neat, plug-and-play &amp;quot;Grove connectors.&amp;quot; We just use the provided 4-pin cables to snap our sensors and output devices into the board.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg45tag2"&gt;2. Selecting the Ideal &amp;quot;Alert&amp;quot; Modules&lt;/h2&gt;
&lt;p&gt;While the Grove Kit is known for its &amp;quot;sensors&amp;quot; (which take data &lt;em&gt;in&lt;/em&gt;), for an alert system, we need &amp;quot;actuators&amp;quot; (which put data &lt;em&gt;out&lt;/em&gt;). To create a robust, multi-sensory alert system, I have selected the following components from the Grove Starter Kit V3:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Grove - Red LED (Visual Beacon):&lt;/strong&gt; A flashing red light is the universal sign of danger. This will catch the operator&amp;#39;s eye immediately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Grove - Buzzer (Audio Siren):&lt;/strong&gt; Factories and workspaces can be distracting. A loud, piercing auditory alarm ensures the alert is heard even if the operator isn&amp;#39;t looking at the system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Grove - LCD RGB Backlight (Status Display):&lt;/strong&gt; We need to tell the operator &lt;em&gt;why&lt;/em&gt; the alarm is going off. Is it a missing vest? A Red Zone incursion? This screen will display the exact text and change its backlight color to red during an emergency.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Grove - Button (Reset Sensor):&lt;/strong&gt; A physical input sensor. Once the operator acknowledges the alarm and corrects the safety issue, they press this button to silence the system.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="mcetoc_1jqg45tag3"&gt;3. Data Flow &amp;amp; System Architecture&lt;/h2&gt;
&lt;p&gt;Before we code, it helps to visualize how information travels through our system. The UNO Q is special because it has a &amp;quot;Dual-Brain&amp;quot;: a Qualcomm processor (for AI) and an STM32 microcontroller (for hardware).&lt;/p&gt;
&lt;p&gt;Here is the flowchart of our logic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;========================================================================
                      SYSTEM DATA FLOW DIAGRAM
========================================================================

 [ USB Camera ] --&amp;gt; Captures video feed of the work zone
       |
       v
 +---------------------------------------------------+
 |                  ARDUINO UNO Q                    |
 |                                                   |
 |  1. QUALCOMM MPU (Python via App Lab)             |
 |     - Runs AI Object Detection (Glasses/Vest)     |
 |     - Detects &amp;quot;Red Zone&amp;quot; Incursion                |
 |            |                                      |
 |            v (Arduino Bridge API triggers alert)  |
 |            |                                      |
 |  2. STM32 MCU (C++ Sketch via App Lab)            |
 |     - Receives &amp;quot;ALERT&amp;quot; signal                     |
 |     - Controls the Grove hardware pins            |
 +---------------------------------------------------+
       | (Signals sent through Base Shield)
       v
 +---------------------------------------------------+
 |             GROVE BASE SHIELD V3                  |
 +---------------------------------------------------+
       |               |               |             |
   (Port D2)       (Port D3)       (Port I2C)    (Port D4)
       |               |               |             |
       v               v               v             v
   [Grove LED]   [Grove Buzzer]  [Grove RGB LCD] [Grove Button]
  (Visual Alert)  (Audio Siren)  (Text Warning)  (Reset Input)
========================================================================
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="mcetoc_1jqg45tag4"&gt;4. Step-by-Step Implementation Instructions&lt;/h2&gt;
&lt;h3 id="mcetoc_1jqg45tag5"&gt;Step 1: Connect the Hardware&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Unplug your UNO Q from power.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Align and press the Grove Base Shield V3 onto the top of the UNO Q.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using the Grove cables, connect the &lt;strong&gt;Grove LED (with a Red bulb)&lt;/strong&gt; to port &lt;strong&gt;D2&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect the &lt;strong&gt;Grove Buzzer&lt;/strong&gt; to port &lt;strong&gt;D3&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect the &lt;strong&gt;Grove Button&lt;/strong&gt; to port &lt;strong&gt;D4&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect the &lt;strong&gt;Grove RGB LCD&lt;/strong&gt; to any of the &lt;strong&gt;I2C&lt;/strong&gt; ports.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="mcetoc_1jqg45tag6"&gt;Step 2: Open Arduino App Lab&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Connect the UNO Q to your computer via USB-C.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch &lt;strong&gt;Arduino App Lab&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new &amp;quot;App&amp;quot;. App Lab will open a dual-pane window. We will be working in the &lt;strong&gt;C++ Sketch&lt;/strong&gt; pane, which controls the Base Shield hardware.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="mcetoc_1jqg45tag7"&gt;5. Coding Examples: Sketches for Each Sensor/Module&lt;/h2&gt;
&lt;p&gt;Below are the beginner-friendly C++ sketches for each individual module so you can understand how they work in isolation. &lt;em&gt;Note: In App Lab, these are standard Arduino C++ commands.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jqg45tag8"&gt;A. The Visual Beacon (Grove LED)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// --- GROVE LED SKETCH ---
// This sketch flashes the LED to simulate an emergency beacon.

// 1. Define the pin where the LED is connected
const int ledPin = 2; // Port D2 on the Base Shield

void setup() {
  // setup() runs once when the board turns on.
  // We tell the UNO Q that the ledPin is an OUTPUT (sending electricity out)
  pinMode(ledPin, OUTPUT); 
}

void loop() {
  // loop() runs over and over forever.
  digitalWrite(ledPin, HIGH); // Turn the LED ON (HIGH = electricity flowing)
  delay(500);                 // Wait for 500 milliseconds (half a second)
  digitalWrite(ledPin, LOW);  // Turn the LED OFF (LOW = no electricity)
  delay(500);                 // Wait for 500 milliseconds
}
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="mcetoc_1jqg45tag9"&gt;B. The Audio Siren (Grove Buzzer)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// --- GROVE BUZZER SKETCH ---
// This sketch sounds a repeating alarm tone.

const int buzzerPin = 3; // Port D3 on the Base Shield

void setup() {
  pinMode(buzzerPin, OUTPUT); // The buzzer receives signals, so it&amp;#39;s an OUTPUT
}

void loop() {
  digitalWrite(buzzerPin, HIGH); // Turn buzzer ON (makes a loud beep)
  delay(200);                    // Wait 0.2 seconds
  digitalWrite(buzzerPin, LOW);  // Turn buzzer OFF
  delay(200);                    // Wait 0.2 seconds
}
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="mcetoc_1jqg45taga"&gt;C. The Status Display (Grove RGB LCD)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// --- GROVE RGB LCD SKETCH ---
// This sketch changes the screen color to Red and prints a warning.

// We need to include special libraries (instruction manuals) for the screen and I2C communication.
#include &amp;lt;Wire.h&amp;gt;
#include &amp;quot;rgb_lcd.h&amp;quot;

rgb_lcd lcd; // Create an LCD object to control the screen

void setup() {
  // Initialize the screen: 16 columns and 2 rows of characters
  lcd.begin(16, 2);
  
  // Set the backlight color to RED (Red, Green, Blue values from 0-255)
  lcd.setRGB(255, 0, 0); 
  
  // Print our warning message on the top row
  lcd.print(&amp;quot;! ALARM ACTIVE !&amp;quot;);
  
  // Move the invisible cursor to the start of the bottom row (column 0, row 1)
  lcd.setCursor(0, 1);
  lcd.print(&amp;quot;RED ZONE BREACH&amp;quot;);
}

void loop() {
  // Nothing needs to loop here, the text stays on the screen!
}
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="mcetoc_1jqg45tagb"&gt;D. The Alarm Reset (Grove Button)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// --- GROVE BUTTON SKETCH ---
// This sketch reads the physical world (sensor input) to check if a button is pressed.

const int buttonPin = 4; // Port D4 on the Base Shield

void setup() {
  pinMode(buttonPin, INPUT); // The button sends data IN to the board
  Serial.begin(9600);        // Open a communication line to our computer screen
}

void loop() {
  // Read the state of the button (HIGH = pressed, LOW = unpressed)
  int buttonState = digitalRead(buttonPin);
  
  if (buttonState == HIGH) {
    Serial.println(&amp;quot;Reset Button Pressed!&amp;quot;); // Send a message to the computer
  }
  delay(100); // Small delay to prevent reading the button too fast
}
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="mcetoc_1jqg45tagc"&gt;6. The Final Implementation: Bringing it All Together&lt;/h2&gt;
&lt;p&gt;In a real-world scenario on the UNO Q, the AI camera detects the missing safety glasses and flips an internal variable to &lt;code&gt;true&lt;/code&gt;. Here is the combined App Lab C++ sketch that pulls all our modules together into one cohesive safety system!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ====================================================================
// FULL SAFETY ALERT SYSTEM SKETCH (Arduino App Lab C++)
// ====================================================================

#include &amp;lt;Wire.h&amp;gt;
#include &amp;quot;rgb_lcd.h&amp;quot;

// Define Hardware Pins
const int ledPin = 2;
const int buzzerPin = 3;
const int buttonPin = 4;

// Initialize LCD
rgb_lcd lcd;

// This variable acts as our system state. 
// In a full App Lab project, the Python AI script would change this variable 
// via the Arduino Bridge when it detects a missing hardhat or Red Zone breach.
bool alarmTriggered = true; 

void setup() {
  // Configure Pins
  pinMode(ledPin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  
  // Start LCD and default to a Green &amp;quot;Safe&amp;quot; state
  lcd.begin(16, 2);
  setSafeState();
}

void loop() {
  // Check if the operator pressed the reset button
  int buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH &amp;amp;&amp;amp; alarmTriggered == true) {
    // If button pressed during an alarm, clear the alarm
    alarmTriggered = false;
    setSafeState();
  }

  // If the alarm is active, flash the lights and sound the buzzer
  if (alarmTriggered == true) {
    triggerAlarmCycle();
  }
}

// Custom function to handle the &amp;quot;All Clear&amp;quot; state
void setSafeState() {
  digitalWrite(ledPin, LOW);    // Turn off LED
  digitalWrite(buzzerPin, LOW); // Turn off Buzzer
  
  lcd.setRGB(0, 255, 0);        // Set LCD to Green
  lcd.clear();                  // Clear old text
  lcd.print(&amp;quot;SYSTEM SECURE&amp;quot;);   // Print safe message
  lcd.setCursor(0, 1);
  lcd.print(&amp;quot;Monitoring...&amp;quot;);
}

// Custom function to handle the flashing/beeping of the alarm
void triggerAlarmCycle() {
  // Set LCD to Red and display warning
  lcd.setRGB(255, 0, 0);
  lcd.setCursor(0, 0);
  lcd.print(&amp;quot;! SAFETY ALERT !&amp;quot;);
  lcd.setCursor(0, 1);
  lcd.print(&amp;quot;ZONE BREACH     &amp;quot;);

  // Flash ON
  digitalWrite(ledPin, HIGH);
  digitalWrite(buzzerPin, HIGH);
  delay(300);
  
  // Flash OFF
  digitalWrite(ledPin, LOW);
  digitalWrite(buzzerPin, LOW);
  delay(300);
}
&lt;br class="ProseMirror-trailingBreak" /&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="mcetoc_1jqg45tagd"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;By utilizing the Grove Base Shield V3, we bypass complex wiring and focus strictly on logic and implementation. The dual architecture of the UNO Q allows the heavy lifting of AI detection to occur behind the scenes, seamlessly triggering our STM32 microcontroller to fire off real-world visual, auditory, and textual alerts using our Grove actuators.&lt;/p&gt;
&lt;p&gt;I&amp;#39;d love to hear feedback from other challengers! Have any of you experimented with adding the Grove Smart Relay to actually shut down machinery during a Red Zone incursion?&lt;/p&gt;
&lt;p&gt;Looking forward to your comments!&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 3 - PCB Schematics Walkthrough</title><link>https://community.element14.com/thread/57020?ContentTypeID=0</link><pubDate>Sun, 07 Jun 2026 04:00:31 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:32b5052e-87c0-408b-b639-4de6165b020f</guid><dc:creator>arvindsa</dc:creator><slash:comments>5</slash:comments><comments>https://community.element14.com/thread/57020?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57020/solarsense---part-3---pcb-schematics-walkthrough/rss?ContentTypeId=0</wfw:commentRss><description>&lt;h2 id="mcetoc_1jqbvjvms0"&gt;Recap:&lt;/h2&gt;
&lt;p data-start="52" data-end="219"&gt;I&amp;rsquo;m developing a smart solar monitoring system that uses a clean reference panel to eliminate weather effects and directly quantify dust-induced losses in real time. With edge AI and environmental sensing, the goal is to predict the &lt;em data-start="288" data-end="295"&gt;right&lt;/em&gt; moment to cleanbefore efficiency drops enough to matter.&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="52" data-end="219"&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;li data-start="52" data-end="219"&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can" data-e14adj="t"&gt;SolarSense - Part 2 - Can Arduino CAN?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sunday Arrives, Time for a leisurely writing session. The Data collection unit has to survive the forces unleashed by mother nature. I am not talking about hurricane but since i am living in Kerala, India where the weather ranges from dry to 100% humidity, heavy monsoon rainfall, temperatures upto 50deg C (which means inside a casing it can go upto even 60deg). TO me this project is close to heart and therefore i decided to invest in a good PCB. In this Post I shall walk you through the design of the schematics.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg0jdio0"&gt;Sheet 1: Panel Current Measurement and MPTT Charger&lt;/h2&gt;
&lt;p&gt;The Primary aim would be to measure the current of the clean and unclean panel under different environmental sensor reading. Ideally, if a solar panel is uniformly soiled, only the current output should decreased as opposed to reduction in voltage, only when one of the cells inside it is covered, the voltage produced by that drops leading to a drop in the overall voltage output of the panel. SO under that assumption, i am gonna wire the 3 panels in parallel with an SS14 Diode to prevent the clean panels output back-powering the unclean one. The Current is measured by a 100mOhm 1% resistor, 2W 2512 Package. Using INA219B (not INA219A) which has a differential output of +-320mV, it can measure upto 3.2mA, with each panel expected upto 300mA, it is way over the necessary point.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The INA219 in 12-bit mode with PGA = /1 (32 mV full-scale differential) will provide -&amp;nbsp;&amp;nbsp;full-scale / 2^15 = 3.2 A / 32768 = 97.6 &amp;micro;A per LSB (appx)&lt;/p&gt;
&lt;p&gt;To Charge the LiPo for self powering, I am using the popular CN3791. Not the best, but fastest to source.&amp;nbsp;The key specification is that it regulates the panel input to approximately 80 % of the open-circuit voltage&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Programming the charging current:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The charge current is set by a resistor on the PROG pin:&amp;nbsp;&amp;nbsp;&lt;code&gt;R_PROG = 2800 / I_CHG (&amp;Omega;)&amp;nbsp;&lt;/code&gt;For a 1 A charge current: R_PROG = 2.8 k&amp;Omega; -&amp;gt; use 2.7 k&amp;Omega; standard.&lt;/p&gt;
&lt;p&gt;For a 500 mA charge current: R_PROG = 5.6 k&amp;Omega;. I am targeting 500 mA for now because the panel I have is small, so R_PROG = 5.6 k&amp;Omega;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Panel input capacitor:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The CN3791 datasheet recommends a bulk cap on VIN. A 47 &amp;micro;F electrolytic in parallel with 100 nF ceramic. The CN3791 has a relatively slow MPPT loop, so you need enough bulk capacitance to stop the input rail from collapsing during transients. I will be adding that electrolytic cap soon.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:600px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1200/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0002.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg185bh1"&gt;Sheet 2: Power Management&lt;/h2&gt;
&lt;p&gt;Starting off with MAX17048 to keep track of the LiPo to ensure that the system is having enough power. Based on a crude calculations, using a 2000mAH battery should have enough power to run the system for roughly 30~40 hours. So It should get two session with sunshine, hopefully enough to charge it. In case the Battery drops low, i will get to know via the comms and then i can go swap it out, For the HX94C requires an excitation voltage between 6~30V and i decided to go with 12V and so i use a TPS6104 To boost the LiPo to 12V, and for enough efficiency, AP2112 for the 3.3V rail. Also added in a TPS63020 circuit to test it out. it is not an important part to this circuit but i wanted to trial the circuit for another project. :D&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:567px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1134/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0004.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg1voff2"&gt;Sheet 3:&amp;nbsp;STM32L476RGT6&lt;/h2&gt;
&lt;p&gt;This Choice of MCU was thankfully easy, as i had many STM32s on hand and&amp;nbsp;STM32L476RGT6 was the best suited MCU with CAN Controller.&amp;nbsp; I am using a CR2032 Battery for RTC Power for the STM32 to keep track of time.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:567px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1134/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0003.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg254843"&gt;Sheet 4:&amp;nbsp;Communications and Display&lt;/h2&gt;
&lt;p&gt;I am using the Waveshare RS485/CAN shield for initial prototyping and have broken out the CAN_TX and CAN_RX lines as global labels connected to the STM32 FDCAN peripheral.&amp;nbsp; For long term storage, as a backup in case comms fail, i am using a Micro SD Card, will be using the SDMMC mode rather than the SPI mode. This will be new to me so I am excited to make the code for it. The OLED is only for Debug purposes and not for actual deployment. Thinking few months down the line, i do want to deploy a few of the panels in different locations to&amp;nbsp; actually test the AI model. It will be remote enough that i cant pull a CAN bus to it, So i have added support for WioSX1262 Lora. With a Power usage of 120mA (396mW) it still is comfortable within the limit of my power budget.&lt;/p&gt;
&lt;p&gt;Why am i use CAN if i can use Lora? For making an AI model the more data i have it is better, and to figure out the best feature extraction parameters, i need to experiment with the RAW data, which means lots of Frequent Data, Lora is not built for that, but while deploying the AI model, features are calculated and classification done on the edge device, LORA only transmits the status updates, not the entire data. Therefore LoRa will work there.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:567px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1134/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0005.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg2tb0t4"&gt;Sheet 5: Sensors&lt;/h2&gt;
&lt;p&gt;To Interface the&amp;nbsp;&lt;a id="e14-product-link-539a3" class="e14-embedded e14_shopping-cart-far e14-link" href="https://referral.element14.com/OrderCodeView?fsku=3795213&amp;amp;nsku=30AC8630&amp;amp;COM=e14c-e14-on-the-line&amp;amp;CMP=e14c-e14-on-the-line&amp;amp;osetc=e14c-e14-on-the-line" data-at-areainteracted="rte-content" data-at-type="click" data-at-link-type="link" data-at-label="PRODUCT_POPUP_OPEN" data-farnell="3795213" data-newark="30AC8630" data-comoverride="e14-on-the-line" data-cmpoverride="e14-on-the-line" data-cpc="" data-avnetemea="" data-avnetema="" data-avnetasia="" target="_blank" data-e14adj="t"&gt;OmegaDwyer 5SRTC-TT-K-24-72 Thermocouple, K, 0 &amp;deg;C, 260 &amp;deg;C, 80 &amp;quot;, 2 m (Pack of 5)&lt;/a&gt;&amp;nbsp;sensors i am using MAX31855. For now i am gonna be using only 1 to keep track of the temperature of one solar panel, but i put in more of this as&amp;nbsp; DNP just to ensure future use. Maybe LiPO temperature?&amp;nbsp; BMP280 to Keep track of Pressure. Thankfully looking at the I2C Addesses, there does not seem to be any&amp;nbsp; conflicts and i can keep all of them in one I2C bus, there is an issue that SEN66 communicates only at&amp;nbsp;100 kHz I2C only. but that&amp;#39;s alright it can be handled in the code.&amp;nbsp; I an extra I2C connector for future use.&lt;/p&gt;
&lt;p&gt;&lt;a id="e14-product-link-7ba67" class="e14-embedded e14_shopping-cart-far e14-link" href="https://referral.element14.com/OrderCodeView?fsku=3463003&amp;amp;nsku=50AC8704&amp;amp;COM=e14c-e14-on-the-line&amp;amp;CMP=e14c-e14-on-the-line&amp;amp;osetc=e14c-e14-on-the-line" data-at-areainteracted="rte-content" data-at-type="click" data-at-link-type="link" data-at-label="PRODUCT_POPUP_OPEN" data-farnell="3463003" data-newark="50AC8704" data-comoverride="e14-on-the-line" data-cmpoverride="e14-on-the-line" data-cpc="" data-avnetemea="" data-avnetema="" data-avnetasia="" target="_blank" data-e14adj="t"&gt;OmegaDwyer Humidity Sensor, CURRENT, 30V, W/4P CONN&lt;/a&gt;&amp;nbsp;requires a sense resistor for each Relative Humidity and Temperature. This sensor is one of the best for both RH and Temperature even though Temperature can be measured by BMP and SEN66. As per their Datasheet to get a ADC reading between 0-3.3V i have use 150Ohm Resistors.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I also added a rain detector connector with a digital and analog pin capability again in case i need to extend the model further - The rain detection module is like&amp;nbsp;&lt;a id="" href="https://arduinogetstarted.com/tutorials/arduino-rain-sensor" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://arduinogetstarted.com/tutorials/arduino-rain-sensor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last but not the least, as a secondary source of Incident light, an alternative for clean panel is the UV Sensor which works on Analog. I added a connector to it,&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;0x3C — SSD1306 OLED
0x40 — INA219 (panel current/voltage)
0x6B — SEN66 (air quality)
0x76 — BMP280 (pressure, ambient temperature)&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:567px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1134/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0006.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg3aje75"&gt;Sheet 0: The Root Sheet gluing everything together&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:567px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x1134/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb_5F00_page_2D00_0001.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Here is the final architecture&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:350px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x700/__key/communityserver-discussions-components-files/453/pastedimage1780804456312v1.png" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqg3igtr6"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;This PCB is a leap of faith, usually i breadboard some of the circuits before i make a PCB and in particular i have not used the HX94C sensor yet, but no first design of PCB is perfect, so let&amp;#39;s bank on one more round of PCB design. I am gonna verify the circuit again before layout and Fab. Tomorrow I will finish the CAN receiver and benchmark the data speeds and throughput.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqin3dgg0"&gt;Update: PCB Layout and Routing&lt;/h2&gt;
&lt;p&gt;Hey, I managed to place and route all the components, I gave the order to manufacture via PCB Way in white Soldermask. I chose white because, it would absorb heat less and therefore should remain cool(er)&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:435px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x870/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:450px;max-width:800px;"  src="https://community.element14.com/resized-image/__size/1600x900/__key/communityserver-discussions-components-files/453/solar_2D00_sense_2D00_pcb2.png" /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>SolarSense - Part 2 - Can Arduino CAN?</title><link>https://community.element14.com/thread/57018?ContentTypeID=0</link><pubDate>Sat, 06 Jun 2026 18:43:23 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:21b35a2e-0ccc-4210-85b1-078b2fb59dab</guid><dc:creator>arvindsa</dc:creator><slash:comments>7</slash:comments><comments>https://community.element14.com/thread/57018?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57018/solarsense---part-2---can-arduino-can/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;I spent a lot of time last month cleaning up my projects. I almost lost lost track of time. Now to convert the POC I made into a reliable data collection powerhouse, ArduinoQ running Zephyr on STM32 is new to me. Linux part i can handle.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqbvjvms0"&gt;Recap:&lt;/h2&gt;
&lt;p data-start="52" data-end="219"&gt;I&amp;rsquo;m developing a smart solar monitoring system that uses a clean reference panel to eliminate weather effects and directly quantify dust-induced losses in real time. With edge AI and environmental sensing, the goal is to predict the &lt;em data-start="288" data-end="295"&gt;right&lt;/em&gt; moment to cleanbefore efficiency drops enough to matter.&lt;/p&gt;
&lt;ul&gt;
&lt;li data-start="52" data-end="219"&gt;&lt;a href="https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57013/solarsense---part-1---introduction-the-poc-built-and-the-plan" data-e14adj="t"&gt;SolarSense - Part 1 - Introduction, The POC Built and The Plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcetoc_1jqf1cmer0"&gt;Setting Up Arduino Q&lt;/h2&gt;
&lt;p&gt;I downloaded AppLab quickly from the official website, connected the Arduino Q via a USB-C and blasted away through the configuration screen. In this regard, it was easy, although the update took time.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Arduino_5F00_App_5F00_Lab_5F00_Eg9pR0LCjD.png" /&gt;&lt;/p&gt;
&lt;p&gt;And it is always an Ritual that i need to blink an LED whenever i get a new hardware and so I used the example. I have to admit it, Using Zephyr , An Python Code and RPC connection between them just to blink LED is like using cannon to Kill a mosquito.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Arduino_5F00_App_5F00_Lab_5F00_Z6hIRe9Fjh.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqf1cmer1"&gt;Internet Research to Answer the Question&lt;/h2&gt;
&lt;p&gt;After Looking at some Internet on how to get CAN to work, I came to a conclusion that ArduinoQ cannot run the CAN Controller on the STM32&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a id="" href="https://forum.arduino.cc/t/arduino-uno-q-canfd-controller-use/1414493" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://forum.arduino.cc/t/arduino-uno-q-canfd-controller-use/1414493&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a id="" href="https://forum.arduino.cc/t/uno-q-can-library/1416035" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://forum.arduino.cc/t/uno-q-can-library/1416035&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a id="" href="https://forum.arduino.cc/t/trying-to-get-fdcan-working-on-the-uno-q/1431867" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://forum.arduino.cc/t/trying-to-get-fdcan-working-on-the-uno-q/1431867&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so the challenge begins. If i cannot easily interface the CAN Peripheral on STM32 then my next idea became to have an additional board like Teensy 3.2 or my Nucleo F303Re which as a CAN Peripheral as a bridge between ArduinoQ and the&amp;nbsp;MAX33041E. Then the number of bridges made me uneasy. Imagine &lt;code&gt;Q&amp;#39;s Qualcomm Chip -&amp;gt; Q&amp;#39;s STM32 Chip -&amp;gt; Nucleo F303RE -&amp;gt; MAX33041E -&amp;gt; CAN Bus&lt;/code&gt;. It was ridiculous. I looked if i can break the Qualcomm&amp;#39;s Bridge and somehow run the STM32 using STM32HAL. I came to know that the Linux installation contains OpenOCD and Zephyr&amp;#39;s Documentation that the Physical connection between MPU and MCU is LPUART managed by an arduino-router service on the linux&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_06_5F00_21_2D00_25_2D00_58.png" /&gt;&lt;/p&gt;
&lt;h2 id="mcetoc_1jqf28mg52"&gt;Taking Control of the STM32 from the hands of Qualcomm Chip&lt;/h2&gt;
&lt;h3 id="mcetoc_1jqf2965l3"&gt;Step 1: Disable arduino-router Service&lt;/h3&gt;
&lt;p&gt;I connected to the ArduinoQ&amp;#39;s terminal via SSH&lt;code&gt;&amp;nbsp;ssh arduino@&amp;lt;boardname&amp;gt;.local&lt;/code&gt;&amp;nbsp;where boardname is the name you gave it while setup. The Password is again the same as you gave in the setup&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="batchfile"&gt;sudo cp /etc/systemd/system/arduino-router.service \
        /home/arduino/arduino-router.service.bak
sudo rm /etc/systemd/system/arduino-router.service
sudo systemctl mask arduino-router
sudo systemctl daemon-reload
sudo pkill -9 -f arduino-router&lt;/pre&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_06_5F00_21_2D00_40_2D00_37.png" /&gt;&lt;/p&gt;
&lt;p&gt;I ensured that i restarted the Q to ensure that the service does not pop up back again.&lt;/p&gt;
&lt;h3 id="mcetoc_1jqf2bgk24"&gt;Step 2: Setting up the STM32MX Project&lt;/h3&gt;
&lt;p&gt;From the Schematics of Arduino Q here: &lt;a id="" href="https://docs.arduino.cc/resources/schematics/ABX00162-schematics.pdf" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://docs.arduino.cc/resources/schematics/ABX00162-schematics.pdf&lt;/a&gt; , I got the full chip code -&amp;nbsp;STM32U585AII6TR, set up the project on STM32MX with the clock and just LPUART, LED, CAN&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_06_5F00_21_2D00_33_2D00_48.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/Screenshot_5F00_2026_2D00_06_2D00_06_5F00_21_2D00_31_2D00_20-_2D00_-Copy.png" /&gt;\&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The clock values as as below&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM       = 1;
RCC_OscInitStruct.PLL.PLLN       = 10;
RCC_OscInitStruct.PLL.PLLR       = 1;   // 16 * 10 / 1 = 160 MHz&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I generated the code, starting with a simple blink. btw,&amp;nbsp;Flash latency must be &lt;code&gt;FLASH_LATENCY_4&lt;/code&gt; at VOS1 / 160 MHz &amp;mdash; anything lower causes a hard fault on the first cache miss.&lt;/p&gt;
&lt;h3 id="mcetoc_1jqf2leaa5"&gt;Step 3: The Blinking Ritual&lt;/h3&gt;
&lt;p&gt;The onboard LED is on PH10, active-HIGH&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/pastedimage1780770188663v1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/* MX_GPIO_Init USER CODE section */
__HAL_RCC_GPIOH_CLK_ENABLE();

GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin   = GPIO_PIN_10;
GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull  = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOH, &amp;amp;GPIO_InitStruct);

/* Main loop */
HAL_GPIO_TogglePin(GPIOH, GPIO_PIN_10);
HAL_Delay(500);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jqf2qmqr7"&gt;Step 4: Setting up the LPUART1&lt;/h3&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;/* USER CODE BEGIN Includes */
#include &amp;lt;stdio.h&amp;gt;
/* USER CODE END Includes */

/* USER CODE BEGIN 2 */
char tx_buf[64];
/* USER CODE END 2 */

/* Main loop */
int len = snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hello from STM32\r\n&amp;quot;);
HAL_UART_Transmit(&amp;amp;hlpuart1, (uint8_t *)tx_buf, len, 100);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="python"&gt;#!/usr/bin/env python3
import serial, sys

ser = serial.Serial(&amp;#39;/dev/ttyHS1&amp;#39;, 209700, timeout=5)
print(f&amp;quot;Listening on /dev/ttyHS1 at 209700 baud...&amp;quot;)
try:
    while True:
        line = ser.readline()
        if line:
            print(line.decode(&amp;#39;utf-8&amp;#39;, errors=&amp;#39;replace&amp;#39;).strip())
except KeyboardInterrupt:
    ser.close()&lt;/pre&gt;&lt;/p&gt;
&lt;h3 id="mcetoc_1jqf330ft8"&gt;Step 5: Now ArduinoQ CAN&lt;/h3&gt;
&lt;p&gt;The STM32 has CAN pins on PA11 (RX) / PA12 (TX). Before connecting to&amp;nbsp;a real bus, verify the peripheral and transceiver with an external loopback test.&amp;nbsp;External loopback drives the TX pin through the transceiver while simultaneously receiving&lt;br /&gt;the frame internally. The STM32 provides its own ACK, so it keeps transmitting even with no termination resistor or other node on the bus. And Ensure that the Jumpers are all in default positions as&amp;nbsp;&lt;a id="" href="https://www.analog.com/media/en/technical-documentation/data-sheets/MAX33041ESHLD.pdf" rel="noopener noreferrer nofollow" target="_blank" data-e14adj="t"&gt;https://www.analog.com/media/en/technical-documentation/data-sheets/MAX33041ESHLD.pdf&lt;/a&gt;&amp;nbsp;except for JU11, you need to switch it to the 3.3V side so that it can receive the power from Arduino Q&amp;#39;s 3.3V&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260606_5F00_231213-_2D00_-Copy.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;FDCAN_FilterTypeDef sFilterConfig;
sFilterConfig.IdType       = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex  = 0;
sFilterConfig.FilterType   = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1    = 0x000;
sFilterConfig.FilterID2    = 0x000;  // mask=0 → accept all standard frames
HAL_FDCAN_ConfigFilter(&amp;amp;hfdcan1, &amp;amp;sFilterConfig);
HAL_FDCAN_Start(&amp;amp;hfdcan1);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;// User Loop
FDCAN_TxHeaderTypeDef txHeader;
uint8_t canTxData[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04};
txHeader.Identifier         = 0x123;
txHeader.IdType             = FDCAN_STANDARD_ID;
txHeader.TxFrameType        = FDCAN_DATA_FRAME;
txHeader.DataLength         = FDCAN_DLC_BYTES_8;
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch      = FDCAN_BRS_OFF;
txHeader.FDFormat            = FDCAN_CLASSIC_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
txHeader.MessageMarker      = 0;

HAL_StatusTypeDef tx_status =
    HAL_FDCAN_AddMessageToTxFifoQ(&amp;amp;hfdcan1, &amp;amp;txHeader, canTxData);
HAL_Delay(10);

FDCAN_RxHeaderTypeDef rxHeader;
uint8_t canRxData[8];
int len;

if (tx_status != HAL_OK) {
    len = snprintf(tx_buf, sizeof(tx_buf), &amp;quot;CAN TX ERR %d\r\n&amp;quot;, (int)tx_status);
} else if (HAL_FDCAN_GetRxFifoFillLevel(&amp;amp;hfdcan1, FDCAN_RX_FIFO0) &amp;gt; 0) {
    HAL_FDCAN_GetRxMessage(&amp;amp;hfdcan1, FDCAN_RX_FIFO0, &amp;amp;rxHeader, canRxData);
    len = snprintf(tx_buf, sizeof(tx_buf), &amp;quot;CAN OK id=0x%03lX d=%02X%02X%02X%02X\r\n&amp;quot;,
                   (unsigned long)rxHeader.Identifier,
                   canRxData[0], canRxData[1], canRxData[2], canRxData[3]);
} else {
    len = snprintf(tx_buf, sizeof(tx_buf), &amp;quot;CAN FAIL no rx\r\n&amp;quot;);
}
HAL_UART_Transmit(&amp;amp;hlpuart1, (uint8_t *)tx_buf, len, 100);&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;and we get the&amp;nbsp;&lt;code&gt;CAN OK id=0x123 d=DEADBEEF &lt;/code&gt;on the serial output. And on the Oscilloscope I get the beautiful differential signal.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" alt="image" style="display:block;margin-left:auto;margin-right:auto;max-height:360px;max-width:640px;"  src="https://community.element14.com/resized-image/__size/1280x720/__key/communityserver-discussions-components-files/453/20260606_5F00_225714-_2D00_-Copy.jpg" /&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Finally we made the ArduinoQ CAN.&lt;/p&gt;
&lt;h2 id="mcetoc_1jqf3oj009"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Arduino Q looking at the specs seems promising but there are lot of usability issues with the software and they should have given an easy connection to STM32 for power users. None the less, I managed to overcome the limitation Zephyr OS gave - Ability to use the CAN peripheral on the STM32 and in that process, i unleashed full potential of the STM32. Next will be to actually connect an Receiver side to the CAN Bus and establish and transfer some poetry.&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>Forum#3: UNO Q &amp; MAX33041ESHLD CAN Bus Integration - ILS</title><link>https://community.element14.com/thread/57015?ContentTypeID=0</link><pubDate>Fri, 05 Jun 2026 04:12:22 GMT</pubDate><guid isPermaLink="false">93d5dcb4-84c2-446f-b2cb-99731719e767:8f837e2b-e449-45eb-b876-bd96ac5021a7</guid><dc:creator>skruglewicz</dc:creator><slash:comments>3</slash:comments><comments>https://community.element14.com/thread/57015?ContentTypeID=0</comments><wfw:commentRss>https://community.element14.com/challenges-projects/design-challenges/on-the-line/f/forum/57015/forum-3-uno-q-max33041eshld-can-bus-integration---ils/rss?ContentTypeId=0</wfw:commentRss><description>&lt;p&gt;&lt;span style="font-weight:400;"&gt;Forum#3: UNO Q &amp;amp; MAX33041ESHLD CAN Bus Integration - ILS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Published:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; June 5, 2026&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Project:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Industrial Line Sentinel (ILS)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Welcome back to the third installment of the Industrial Line Sentinel (ILS) project!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;In my previous posts, I discussed the system architecture and my LabVIEW supervisory dashboard. Today, I am getting my hands dirty with the hardware. My goal is to establish the industrial communication backbone for the ILS by successfully integrating the &lt;/span&gt;&lt;b&gt;Arduino UNO Q&lt;/b&gt;&lt;span style="font-weight:400;"&gt; with the &lt;/span&gt;&lt;b&gt;Analog Devices MAX33041ESHLD# CAN Transceiver Shield&lt;/b&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;In this post, I will explain the basics of CAN bus architecture, demonstrate how to physically piggyback the shield onto the UNO Q, and provide a &amp;quot;Hello World&amp;quot; code example that reads data from a &lt;/span&gt;&lt;b&gt;Grove Temperature Sensor&lt;/b&gt;&lt;span style="font-weight:400;"&gt; (from the Grove Starter Kit) and broadcasts it onto the CAN network. &lt;/span&gt;&lt;b&gt;I encourage you to follow along and implement this experiment to understand the code and hardware setup.&lt;/b&gt;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;CAN Bus Architecture: A Novice-Friendly Deep Dive&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;If you are new to industrial protocols, you might be wondering: &lt;/span&gt;&lt;i&gt;&lt;span style="font-weight:400;"&gt;Why use CAN Bus instead of standard USB, I2C, or SPI?&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Controller Area Network (CAN)&lt;/b&gt;&lt;span style="font-weight:400;"&gt; was originally developed by Bosch for the automotive industry to reduce the heavy wiring harnesses in cars. Think of it as a highly robust &amp;quot;nervous system&amp;quot; for machines.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Here is why it is the gold standard for industrial environments:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;No Central Boss (Multi-Master):&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Unlike USB (which needs a host PC) or SPI (which has a Master/Slave relationship), every device (called a &lt;/span&gt;&lt;b&gt;Node&lt;/b&gt;&lt;span style="font-weight:400;"&gt;) on a CAN bus can speak up whenever the bus is free.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Message Priority:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Instead of addressing messages to specific devices, nodes broadcast messages tagged with an &lt;/span&gt;&lt;b&gt;ID&lt;/b&gt;&lt;span style="font-weight:400;"&gt;. Lower ID numbers have higher priority. If a &amp;quot;Machine Overheating&amp;quot; node (ID 0x01) and a &amp;quot;Temperature Reading&amp;quot; node (ID 0x50) try to talk at exactly the same time, the bus hardware automatically prioritizes the overheat message without losing a single bit.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Differential Signaling:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; This is the magic behind CAN&amp;#39;s ruggedness. Instead of sending a voltage relative to ground, CAN uses two twisted wires: &lt;/span&gt;&lt;b&gt;CAN High (CAN_H)&lt;/b&gt;&lt;span style="font-weight:400;"&gt; and &lt;/span&gt;&lt;b&gt;CAN Low (CAN_L)&lt;/b&gt;&lt;span style="font-weight:400;"&gt;. To send a &amp;quot;1&amp;quot;, both wires sit at 2.5V. To send a &amp;quot;0&amp;quot;, CAN_H goes up to 3.75V and CAN_L drops to 1.25V. If electromagnetic noise from a massive factory motor spikes the line, it spikes &lt;/span&gt;&lt;i&gt;&lt;span style="font-weight:400;"&gt;both&lt;/span&gt;&lt;/i&gt;&lt;span style="font-weight:400;"&gt; wires equally. The receiver only cares about the &lt;/span&gt;&lt;i&gt;&lt;span style="font-weight:400;"&gt;difference&lt;/span&gt;&lt;/i&gt;&lt;span style="font-weight:400;"&gt; between the wires, effectively canceling out the noise!&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Termination:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; To prevent electrical signals from reaching the end of the wire and bouncing back (like an echo in a canyon), the physical ends of the CAN bus must be capped with &lt;/span&gt;&lt;b&gt;120-ohm resistors&lt;/b&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;b&gt;Why the UNO Q + is the Perfect Match&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;The UNO Q has a built-in CAN Controller on its Cortex-M33 real-time core, which formats the data into CAN frames. However, a microcontroller cannot output the voltages required to drive the physical CAN lines.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;That is where the &lt;/span&gt;&lt;b&gt;MAX33041ESHLD#&lt;/b&gt;&lt;span style="font-weight:400;"&gt; comes in. It acts as the physical &amp;quot;mouth and ears&amp;quot; of the system. What makes this specific Analog Devices shield incredible for my &amp;quot;On The Line&amp;quot; challenge is its &lt;/span&gt;&lt;b&gt;&amp;plusmn;40V Fault Protection&lt;/b&gt;&lt;span style="font-weight:400;"&gt;. In a real factory, if a 24V power line accidentally shorts against your communication data lines, standard electronics will instantly fry. The MAX33041E will shrug it off, keeping my ILS node alive.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;Hardware Setup: Piggybacking and Pin Connections&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Proving that I can stack these two boards is my first milestone. Because the MAX33041ESHLD is an Arduino-format shield, it physically snaps right onto the top headers of the UNO Q.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;1. Routing the TX and RX Lines&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;The shield utilizes a digital isolator (the MAX14931) to safely translate logic levels between the UNO Q and the CAN Transceiver. On the shield, look for the &lt;/span&gt;&lt;b&gt;JU1 Header Block&lt;/b&gt;&lt;span style="font-weight:400;"&gt;. This matrix allows you to route the transceiver&amp;#39;s TXD and RXD pins to any digital pin on the Arduino header.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Connection:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Using jumper shunts on JU1, route the transceiver TXD to the UNO Q&amp;#39;s hardware CAN TX pin, and RXD to the UNO Q&amp;#39;s hardware CAN RX pin. (Check your specific UNO Q pinout, but these often map to D1 and D0 or specific SPI/CAN alt-functions).&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;b&gt;2. Connecting the Sensor&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;For this &amp;quot;Hello World&amp;quot; test, I will connect the &lt;/span&gt;&lt;b&gt;Grove Temperature Sensor&lt;/b&gt;&lt;span style="font-weight:400;"&gt; from the Grove Starter Kit.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;i&gt;&lt;span style="font-weight:400;"&gt;Note:&lt;/span&gt;&lt;/i&gt;&lt;span style="font-weight:400;"&gt; This sensor uses a thermistor to measure ambient temperature and outputs a standard analog signal, making it incredibly simple to interface with microcontrollers.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Connect the sensor&amp;#39;s signal pin to the UNO Q&amp;#39;s &lt;/span&gt;&lt;b&gt;Analog Pin A0&lt;/b&gt;&lt;span style="font-weight:400;"&gt;, the VCC pin to the UNO Q&amp;#39;s &lt;/span&gt;&lt;b&gt;5V&lt;/b&gt;&lt;span style="font-weight:400;"&gt; (or 3.3V) pin, and the GND pin to the UNO Q&amp;#39;s &lt;/span&gt;&lt;b&gt;GND&lt;/b&gt;&lt;span style="font-weight:400;"&gt; pin (or simply use a Grove Base Shield).*&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;b&gt;The &amp;quot;Hello World&amp;quot; Experiment: Broadcasting Telemetry&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Now for the software. I will use the Arduino CAN library to initialize the bus at 250 kbps, read the raw analog value from the Grove Temperature sensor, calculate the real-world temperature in Celsius using the thermistor equation, format it into a CAN message, and broadcast it.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style="font-weight:400;"&gt;#include &amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;#include &amp;lt;Arduino_CAN.h&amp;gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;#include &amp;lt;math.h&amp;gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;// Define the analog pin connected to the Grove Temperature sensor&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;const int pinTempSensor = A0;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;// Thermistor constants for Grove Temperature Sensor V1.2&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;const int B = 4275; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // B value of the thermistor&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;const uint32_t R0 = 100000; &amp;nbsp; &amp;nbsp; &amp;nbsp; // R0 = 100k&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;void setup() {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; Serial.begin(115200);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; while (!Serial);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; Serial.println(&amp;quot;ILS Edge Node: Initializing CAN Bus...&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Initialize the CAN Controller at a bit rate of 250 kbps&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; if (!CAN.begin(CanBitRate::BR_250k)) {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.println(&amp;quot;CRITICAL ERROR: Starting CAN failed!&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; while (1); // Halt execution&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; Serial.println(&amp;quot;CAN Bus Started Successfully.&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;}&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;void loop() {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // 1. Read the Grove Temperature Sensor&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; int a = analogRead(pinTempSensor);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Calculate resistance and convert to Temperature (Celsius)&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; float R = 1023.0 / a - 1.0;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; R = R0 * R;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; float temperature = 1.0 / (log(R / R0) / B + 1 / 298.15) - 273.15;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // 2. Construct the CAN Message&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // We assign this node an ID of 0x100. &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // The payload is a 1-byte array containing the temperature in Celsius.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Casting the float to a uint8_t for simple transmission (assuming 0-255 C range).&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Note: This drops the decimal precision, which is fine for a simple Hello World test!&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; uint8_t tempByte = (uint8_t)temperature;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; uint8_t msg_data[] = {tempByte};&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; CanMsg msg(0x100, sizeof(msg_data), msg_data);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // 3. Transmit the message onto the physical bus via the MAX33041E&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; if (CAN.write(msg)) {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.print(&amp;quot;SUCCESS | Sent Temperature Data: &amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.print(temperature);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.println(&amp;quot; C over CAN Bus.&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; } else {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.println(&amp;quot;ERROR | Failed to send CAN message.&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Wait 1 second before broadcasting the next update&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; delay(1000); &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;}&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;Experiment Results&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Upon uploading the code and monitoring the Serial terminal, the UNO Q successfully initialized the internal CAN controller, performed the math to get the ambient temperature, and continuously broadcasted the payload. By setting up a receiver node on the bus and monitoring its Serial terminal, I was able to verify the beautiful differential voltage pulses confirming my data is physically on the wire!&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;Setting Up the Receiver Node with the MAX32666FTHR&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;This board is designed precisely for this type of edge-node application, effectively simulating a complete ILS system with an edge node and a central hub.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;While the MAX32666FTHR has a built-in CAN controller, this board will also require a simple CAN transceiver breakout to interface with the physical bus voltages.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Hardware Connections:&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;CAN Controller Pins:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Connect the MAX32666FTHR&amp;#39;s designated CAN TX and CAN RX pins to the TXD and RXD pins of your transceiver breakout.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Bus Connection:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Connect the transceiver&amp;#39;s CANH and CANL to the shared twisted-pair CAN wires.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;b&gt;Termination:&lt;/b&gt;&lt;span style="font-weight:400;"&gt; Ensure that a 120-ohm termination resistor is placed across CANH and CANL at both physical ends of the bus to prevent signal reflection.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;Receiver Node Code:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;Flash this minimal script to the MAX32666FTHR to listen for the temperature data on the bus (note that exact libraries may vary depending on your chosen board support package or the Maxim SDK):&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style="font-weight:400;"&gt;#include &amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;// Include the specific CAN library for the MAX32666FTHR board support package&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;#include &amp;lt;Maxim_CAN.h&amp;gt; &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;void setup() {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; Serial.begin(115200);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; while (!Serial);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Must match the 250 kbps speed of the transmitter&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; if (!CAN.begin(250000)) {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; Serial.println(&amp;quot;Receiver CAN initialization failed!&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; while (1); &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; Serial.println(&amp;quot;MAX32666FTHR CAN Receiver Listening...&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;}&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;void loop() {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; // Check if a message has arrived on the bus&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; if (CAN.available()) {&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; CanMsg msg = CAN.read();&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; // Filter for our specific Temperature Node ID&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; if (msg.id == 0x100) { &lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(&amp;quot;Received Temp from Node 0x100: &amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.print(msg.data[0]); // Print the 1-byte payload&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; Serial.println(&amp;quot; C&amp;quot;);&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight:400;"&gt;}&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;span style="font-weight:400;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;I have successfully proven that piggybacking the ADI shield onto the UNO Q creates a highly capable, industrially hardened edge node. Although I have published this forum post today, I will continue working through the code example and revising it to better suit my future project goals.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-weight:400;"&gt;I am still very new to this CAN bus architecture, so I would absolutely welcome any suggestions fellow challengers might have on my approach. Maybe someone out there has a better idea on how to receive the CAN messages, or how to pipe this data directly into my LabVIEW dashboard? Let me know in the comments below!&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>