> For the complete documentation index, see [llms.txt](https://beyond-robotix.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://beyond-robotix.gitbook.io/docs/can-ecosystem/arduino-dronecan/thermocouple-tutorial.md).

# Thermocouple Tutorial

A demo of the Arduino DroneCAN library with Beyond Robotix CAN node, using the MCP9600 thermocouple reader.

## Intro

Do you want to integrate a sensor into Ardupilot or PX4? Arduino DroneCAN and the Beyond Robotix CAN node let you do that very quickly. This tutorial runs through integrating the Adafruit MCP9600 thermocouple sensor, resulting in us being able to send temperature messages over DroneCAN. The big advantage of using the Arduino framework is access to Arduino libraries. Almost always, there are libraries available for the sensor you want to use. This saves development time. The Arduino framework is also simple to work with, and the Platformio + VS code platform allows easy development from small to complicated projects.

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2F1SLoeL6GeJCkZqfdrtTt%2Ftitle%20photo-Photoroom.jpg?alt=media&amp;token=4f84f5f8-9042-47b7-8db4-a48c248799e2" alt="" width="375"><figcaption></figcaption></figure>

Using custom firmware allows lots of options, such as integrating a battery monitor + thermocouple and sending all the information with one device in one message. You could action tasks based on the thermocouple onboard the node, e.g. opening a hatch if the battery is getting too hot.

You could integrate GPS receivers, new airspeed sensors, fuel sensors, gas sensors, ESCs and servo nodes.

## Hardware Requirements

* Beyond Robotix CAN Node Dev Kit

{% embed url="<https://www.beyondrobotix.com/products/micro-can-node-development-bundle>" %}

* Flight controller with a CAN bus (Or a CAN 'Sniffer')
* Windows PC is assumed - but other OS’s will apply there might just be some minor adjustments needed
* [Adafruit MCP9600](https://www.adafruit.com/product/4101) + [Thermocouple](https://www.adafruit.com/product/270)

{% hint style="info" %}
You can follow this tutorial on the [CAN Node](/docs/can-ecosystem/l431-can-node.md) instead - it builds with the same environment and the code is identical. Check its page for where to wire the MCP9600.
{% endhint %}

## Getting started

To start, we should download the latest release of Arduino DroneCAN from:

{% embed url="<https://github.com/BeyondRobotix/Arduino-DroneCAN/releases>" %}

The code we will run through is in examples/thermocouple-mcp9600 which is here:

{% embed url="<https://github.com/BeyondRobotix/Arduino-DroneCAN/tree/master/examples/thermocouple-MCP9600>" %}

We’ll need to download a few things to work with the software. We’ve got these setup instructions on our documentation site here:

{% embed url="<https://beyond-robotix.gitbook.io/docs/can-ecosystem/arduino-dronecan>" %}

We’ll also need to get our hardware setup. You’ll need a DroneCAN compatible flight controller or sniffer. We’ll use a Cube Orange. Then connect your Beyond Robotix CAN node to the flight controller via a CAN cable. Also, connect your STLINK to the debug port on the CAN node. Lastly, ensure the switch next to the debug port (`SW1`) is set to '1'.

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FVZlK73k4DCCLbYCl8zvm%2Fhardware-Photoroom.jpg?alt=media&amp;token=a7be79ce-1b31-4dc7-85cf-f7389d60dd5a" alt="" width="375"><figcaption></figcaption></figure>

With the repository downloaded and our tools installed, we can build the default example to make sure it’s all working correctly. The default example sends a "BatteryInfo" message which we'll see later.

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FLVuEImj3e5nEKXVZLcpC%2FPicture1.png?alt=media&amp;token=1b19c0f4-b084-41be-8a18-601083661f5e" alt="" width="563"><figcaption></figcaption></figure>

You should then see “SUCCESS” shown in the terminal. If you don’t, make sure your STLINK is connected correctly and power is being given to the CAN node via the flight controller.

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FmCzMUXIrzgTSflwUsQyp%2FPicture3.png?alt=media&amp;token=871f1b8a-4e0c-48ab-9963-d4cc46ee5fc1" alt="" width="563"><figcaption></figcaption></figure>

We can now see what CAN messages are being sent by the Node. We’ll use DroneCAN GUI tool in this example, however, Mission Planner can also be used for CAN packet inspection.

{% embed url="<https://dronecan.github.io/GUI_Tool/Overview/>" %}

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2Fi3Zqp3nxKthlGwWn7J7s%2FPicture4.png?alt=media&amp;token=99a7ae79-bc66-42ea-9f96-6949bd7ed02f" alt="" width="310"><figcaption></figcaption></figure>

Once connected, we’ll see our Node showing in the list, (you may need to set the Local node ID, press the tick in the top left ish)

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FJaxt4Nsr6PkE1D1K0zhK%2FPicture5.png?alt=media&amp;token=6eedfe0b-7aa7-4d5e-97a6-3aa44e294396" alt="" width="563"><figcaption></figcaption></figure>

We can see the example parameters in Node properties:

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2Fg5dQhIxx3sRyh5f7Hgvv%2FPicture7.png?alt=media&amp;token=9aba53f9-bc3c-4774-b3f7-b3b3fa71150a" alt="" width="375"><figcaption></figcaption></figure>

And we can also see the battery message being sent (which can be found in Tools > Subscriber)

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FdkgEZM37vJDgA3WTWlkI%2FPicture8.png?alt=media&amp;token=1929c694-5fd1-40c9-8d50-3b764f4e3f80" alt="" width="563"><figcaption></figcaption></figure>

We can see the temperature field which responds to the built in MCU temperature sensor. “Voltage” and “current” are showing the raw ADC values from PA1 and PA0 respectively.

## Getting MCP9600 data

We’ve chosen to use the Adafruit library, although there are a few MCP9600 libraries to choose from. Download the latest release, unzip and add it to the “lib” folder of your project. Make sure there are no nested folders! The library can also be installed from the platformio library manager. The “BusIO” library is also needed. Platformio may install this for you.

{% embed url="<https://github.com/adafruit/Adafruit_MCP9600/releases/tag/2.0.4>" %}

{% embed url="<https://github.com/adafruit/Adafruit_BusIO/releases/tag/1.17.2>" %}

Let's have a look at their example...

{% embed url="<https://github.com/adafruit/Adafruit_MCP9600/blob/master/examples/mcp9600_test/mcp9600_test.ino>" %}

They import the files, set the i2c address and setup the `mcp` object. We had to set the address to 0x66 for the MCP9600 we had.

```cpp
#include <Wire.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_I2CRegister.h>
#include "Adafruit_MCP9600.h"

#define I2C_ADDRESS (0x67)

Adafruit_MCP9600 mcp;
```

We then call `.begin` method

```cpp
 /* Initialise the driver with I2C_ADDRESS and the default I2C bus. */
if (! mcp.begin(I2C_ADDRESS)) {
  Serial.println("Sensor not found. Check wiring!");
  while (1);
}
```

then, they have a bunch of other options but the two we're interested in for the basics:

```cpp
 // set our thermocouple type
 mcp.setThermocoupleType(MCP9600_TYPE_K);
 
 // take a temperature measurement
 mcp.readThermocouple()
```

So, with this information, we can now write our DroneCAN application.

## Writing our app

Now, we go to `main.cpp` in Arduino DroneCAN and add the code from above to our `setup()` function. We have provided an example for you to follow:

{% embed url="<https://github.com/BeyondRobotix/Arduino-DroneCAN/blob/master/examples/thermocouple-MCP9600/main.cpp>" %}

In many arduino projects, you might use `delay()`. This can cause problems with Arduino DroneCAN as to needs to perform functions in the background. So, instead of using `delay()`, setup a statement that only runs once a timer counts to a given time, in this example 1000ms. When not meeting this condition, it always calls "dronecan.cycle()" which processes CAN frames in the background and "IWatchdog.reload()" which ensures our watchdog doesn't reset.

In this example, when we are unable to connect to the MCP9600, we want to keep resending the message every second over CAN and serial to say we have a problem. Instead of using a delay, we use an `if` statement which waits until a time has elapsed while still allowing the background tasks to run.

```cpp
if (!mcp.begin(I2C_ADDRESS))
{
    // used to keep track of the time the last debug statement was run 
    uint32_t deadloop = 0;
    
    while (1)
    {
        // see what our current time is
        const uint32_t now = millis();
        
        // check if enough time has passed to activate our debug statements
        if (now - deadloop > 1000)
        {
            // refresh the timer, so the next one will trigger in 1000ms
            deadloop = millis();
            
            dronecan.debug("MCP9600 not found", 0);
            Serial.println("Sensor not found. Check wiring!");
        }
        
        // cycle through recieve and transmit CAN messages
        dronecan.cycle();
        // reset Watchdog to make sure the watchdog doesn't reset us
        IWatchdog.reload();
    }
}
```

The `void loop()` that Arduino uses normally works as you'd expect, so the code is laid out in the usual way. Sensor setup goes in `void setup()`, and the repeating work goes in `void loop()` with `dronecan.cycle()` and `IWatchdog.reload()` at the bottom of it.

```cpp
void setup()
{
    // Set up Code
}

void loop()
{
    // Looping Code

    dronecan.cycle();
    IWatchdog.reload();
}
```

{% hint style="info" %}
Older versions of this library couldn't use `loop()` and needed a `while(true)` at the end of `setup()` instead. If you're following an older tutorial or example that does this, it still works - there's no need to rewrite it.
{% endhint %}

Now, in the example, we've written in some logic which lets you send either a temperature packet or a battery packet depending on some parameters. At the core of it, we send a DroneCAN message like this:

```cpp
uavcan_equipment_device_Temperature pkt{};

pkt.temperature = mcp.readThermocouple();
pkt.device_id = device_id;

sendUavcanMsg(dronecan.canard, pkt);
```

We can see that we read in our thermocouple measurement, and put it into a Temperature packet. You can see the packets available here:

{% embed url="<https://dronecan.github.io/Specification/7._List_of_standard_data_types/>" %}

Although there are lots of different types of packets, not all are actually understood by PX4 or Ardupilot, so you'll have to do some research on the appropriate packet to use. If you get stuck you can contact us at **<admin@beyondrobotix.com>**

We also set the device\_id, which we pull from a parameter in a different part of the loop. This is used by Ardupilot to identify which battery instance to put the temperature into.

We then do our "sendUavcanMsg" which queues our packet.

And there we go! If you compile our example, you'll see either battery or temperature packets coming through depending on the parameters set.

We can create fairly complex features easily : - ) If you use the library and/or CAN node in your project, we'd be keen to hear about it! contact us at **<admin@beyondrobotix.com>**

## Breakpoint debugging

We can debug our programs easily with an STLINK. This is included in the Micro CAN node dev kit. Breakpoint debugging is very useful, being able to see the value of variables in real time as the program runs.

This works as standard in PlatformIO debugging, with your normal build environment. It previously needed a special flash without the bootloader, but this is no longer an issue.

1. You can now use the standard -app environment for your board
2. Set a breakpoint where you're interested in seeing the program state
3. Change to the Debug VS code tab
4. Start the debug session

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2F5Nm5sHdUufYyORL1PNr7%2F1.jpg?alt=media&amp;token=c6e25dc0-e1e4-4260-8377-60e7b206c4ca" alt=""><figcaption></figcaption></figure>

After the program rebuilds, and starts running, the program now stops at where we set our breakpoint. We can see all the in scope variables on the left, and you can even hover over variables in the code to see their value.

<figure><img src="https://1239082692-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdPR57GIwyNGpujTZHIUm%2Fuploads%2FZYoz2o5MuPH4wffrTodJ%2Fimage.png?alt=media&amp;token=782b51db-fdbb-4171-b837-3d73982eaea3" alt=""><figcaption></figcaption></figure>

The bootloader stays on the node throughout, so you can keep updating firmware over CAN while you're developing.

## Folder structure

The project itself is deliberately small. The library, the board definitions and the bootloader binary all live in separate repositories now, and PlatformIO downloads them for you on the first build.

* .pio -> transient, contains all the build files. We can grab our .bin file from here if we want to distribute the program. The downloaded library ends up in ".pio/libdeps"
* .vscode -> vscode config files
* assets -> Just used for our repository for logos etc
* examples -> contains all the code examples that will hopefully help you on how to use the library. Each example contents can be copied to "src/main.cpp" to run them. Bear in mind you may need additional library configuration to run them. This should be in the README of each example folder.
* lib -> any arduino libraries you want to manually download and unzip go here. Create it if it isn't there
* src/main.cpp -> Our core program!
* platformio.ini -> specifies how platformio interacts with the project, including which versions of the library and board definitions to fetch

The things that used to live in this project and have moved out:

* the core "Arduino DroneCAN lib" files, the DroneCAN message headers and libcanard are now in [libArduinoDroneCAN](https://github.com/BeyondRobotix/libArduinoDroneCAN). You can still modify these to extend the library - we're open to PRs : )
* the board definitions, pinmaps, linker scripts and the bootloader binary are now in [br\_platformio\_hwdef](https://github.com/BeyondRobotix/br_platformio_hwdef). This is why there's no "boards", "variants" or "MicroNodeBootloader.bin" in the project any more, and why one upload flashes both the bootloader and your app.
