Go to:

Installation

To use the Nocan library in the Arduino IDE, you should follow the steps outlined in the NoCAN installation tutorial. The tutorial describes how to configure the Arduino IDE board manager to work with NoCAN.

Library

To use the API, include <nocan.h> in your Arduino sketch.

Nocan.open

Synopsis

NocanNodeId Nocan.open()

Description

This function performs 3 main tasks:

  • initialize the I2C interface in master mode,
  • initialize the CAN bus driver (STM32F042)
  • request a node id to be attributed to the node (based on a unique 8-byte serial number).

Return value

This function returns either:

  • a value greater than 0 representing the node id attributed to the node.
  • 0 if no node id was assigned to the node
  • a value less than 0 in case of a hardware initialization issue

Nocan.close

Synopsis

int8_t Nocan.close()

Description

This function currently does nothing.

Nocan.lookupChannel

Synopsis

int8_t Nocan.lookupChannel(const char *channel, NocanChannelId *channel_id) 

Description

This function looks up the value of channel_id corresponding to the string channel.

Return value

It returns 0 in case of success and a negative value in case of failure.

The value of channel_id is updated.

Nocan.registerChannel

Synopsis

int8_t Nocan.registerChannel(const char *channel, NocanChannelId *channel_id) 

Description

This functions registers the channel named channel and obtains the corresponding channel_id.

Return value

It returns 0 in case of success and a negative value in case of failure.

The value of channel_id is updated.

Nocan.unregisterChannel

Synopsis

int8_t Nocan.unregisterChannel(uint8_t channel_id) 

Description

This function unregisters the channel identified by channel_id.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.subscribeChannel

Synopsis

int8_t Nocan.subscribeChannel(NocanChannelId channel_id) 

Description

This function subscribes the node to the channel identified by channel_id.

A node can subscribe to a maximum of 12 channels simultaneously.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.lookupAndSubscribeChannel(const char *channel)

Synopsis

int8_t Nocan.lookupAndSubscribeChannel(const char *channel, NocanChannelId *channel_id) 

Description

This function simply a convenience function that combines the following 2 functions in one: lookupChannel and subscribeChannel.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.unsubscribeChannel

Synopsis

int8_t Nocan.unsubscribeChannel(NocanChannelId channel_id)

Description

This function is the opposite of Nocan.subscribeChannel: it signifies that the node will stop receiving messages from the channel channel_id.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.publishMessage

Synopsis

int8_t Nocan.publishMessage(NocanChannelID cid, const char *str) 

int8_t Nocan.publishMessage(NocanMessage &msg) 

Description

The first form of this function publishes a string str to the channel identified by cid, where str is a standard 0-terminated C string.

The alternate form of the function uses the following structure instead.

typedef struct {
    uint8_t node_id;
    uint16_t channel_id;
    uint8_t data_len;
    uint8_t data[64];
} NocanMessage

It enables to publish a message data of length data_len on the channel identified by channel_id. data_len should be less than or equal to 64. The value of node_id is automatically set by the function, based on the value obtained through Nocan.open().

The first form of the function is typically used to publish standard 0-terminated C strings. In the Arduino platform, a simple way to convert data from a binary type such as "int" or "float" to a string is to use the "String" data type to do the conversion and then extract the converted value with the c_str() method as shown here:

  float pi;
  String temp_string;

  pi = 3.1415;
  pi_string = String(pi, 4);    // transform PI into string

  Nocan.publishMessage(cid, temp_string.c_str());        // publish to the network

For additional ideas, see the examples in the String description of the Arduino reference documentation.

Of course, it is not necessary to use standards C strings. The data field in a NocanMessage can be used to encode any type of data, as long as the total length of the encoded data is less than 64, using the alternate form of the function, which accepts a structure instead of a simple string.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.receiveMessage

Synopsis

int8_t Nocan.receiveMessage(NocanMessage *msg) 

Description

This function allows receiving Nocan messages of up to 64 bytes. It will wait up to 250ms for a message: if no message is received after this delay, the function returns an error. The function Nocan.receivePending() can be used to check if a message is available before calling Nocan.receiveMessage, thereby avoiding a busy wait.

The received message has the following structure:

typedef struct {
    uint8_t node_id;
    uint16_t channel_id;
    uint8_t data_len;
    uint8_t data[64];
} NocanMessage

Where:

  • node_id is the address of the node (see Nocan.open()).
  • channel_id is the channel identifier of the received message.
  • data_len is a number between 0 and 64 describing the length of the message.
  • data is the actual message.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.getUniqueDeviceIdentifier

Synopsis

int8_t Nocan.getUniqueDeviceIdentifier(uint8_t *dest)

Description

This function populates dest with the 8-byte serial number of the device. dest must point to an array of at least 8 bytes.

Return value

It returns 0 in case of success and a negative value in case of failure.

Nocan.receivePending

Synopsis

bool Nocan.receivePending() 

Description

This function returns 1 if an incoming message is pending, and 0 otherwise.

If a message is pending, it can be read with Nocan.receiveMessage().

Return value

1 if a message is pending, 0 otherwise.

Nocan.transmitPending

bool Nocan.transmitPending()

Description

This function returns 1 if the transmit buffer is full, and 0 otherwise.

Return value

1 if a message is pending, 0 otherwise.

Nocan.led

void Nocan.led(bool on)

Description

This function controls the network led (green) of the CANZERO. When on is true, the led is turned on. When on is false, the led is turned off.

Note that this led should not be confused with the user-defined led on the CANZERO (orange).

Return value

None.

Examples

DS18B20 temperature sensor

The code below shows a full sketch of an Omzlo CANZERO application that reads data from a DS18B20 temperature sensor and broadcasts it to the channel called "temperature".

#include <OneWire.h>
#include <DallasTemperature.h>
#include <nocan.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS); 
DallasTemperature sensors(&oneWire);
NocanChannelId tid;

void setup() {
  // put your setup code here, to run once:
  sensors.begin();
  for (;;) 
  {
     if (Nocan.open()>=0)
        break;
     delay(1000);
  }
  Nocan.registerChannel("temperature",&tid);
}

void loop() {
  // put your main code here, to run repeatedly:
  float temp;
  String temp_string;

  sensors.requestTemperatures();    // this takes some time

  temp = sensors.getTempCByIndex(0);
  temp_string = String(temp, 2);    // transform temp into string

  Nocan.publishMessage(tid, temp_string.c_str());        // publish to the network

  delay(1000); 
}

LCD display

Once the above sketch for the DS18B20 is running on an Omzlo CANZERO, you could add a second Omzlo CANZERO node with an LCD shield. It's very simple to publish the temperature from the previous example on the LCD by simply using the following sketch.

#include <LiquidCrystal.h>
#include <nocan.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
NocanChannelId cid;

// Initialization
void setup() 
{
  lcd.begin(16, 2);              
  lcd.print("OMZLO ONE SAYS:");  
  lcd.setCursor(0, 1);
  lcd.print("Initializing....");

  for (;;)
  {
    if (Nocan.open() < 0)
      delay(1000);
    else
      break;
  }

  Nocan.registerChannel("lcd",&cid);
  Nocan.subscribeChannel(cid);

  lcd.setCursor(0, 1);           // set the cursor to column 0, line 1
  lcd.print("Waiting.........");
}

// main loop
void loop() 
{
  int i;
  NocanMessage msg;
  int8_t status = Nocan.receiveMessage(&msg);

  if (status==0)
  {
      if (msg.data_len>16) msg.data_len=16;    
      while (msg.data_len<16) msg.data[msg.data_len++]=' ';
      msg.data[msg.data_len]=0;

      lcd.setCursor(0, 1);
      lcd.print((const char *)msg.data);
  }
}

Controlling a relay

In the following sketch, we create a channel called 'relay'. Sending a message starting with '1' or 'c' on that channel closes the relay while sending a message that starts with '0' or 'o' opens the relay. You can control the relay with commands such as 'nocanc publish relay 1' or 'nocanc publish relay 0'.

The relay is controlled by the Arduino pin 0, which is also the default on our Omzlo MKR Relay Shield, but you can change that pin by simply changing the value of RELAY_PIN.

#include <nocan.h>

#define RELAY_PIN 0

void setup() {
  NocanChannelId cid;

  Nocan.open();
  Nocan.registerChannel("relay", &cid);
  Nocan.subscribeChannel(cid);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);
}

void loop() {
  NocanMessage msg;

  if (Nocan.receivePending())
  {
    Nocan.receiveMessage(&msg);
    if (msg.data_len>=1)
    {
      switch (msg.data[0]) {
        case 'c':
        case 'C':
        case '1':
          digitalWrite(RELAY_PIN, HIGH);
          break;
        case 'o':
        case 'O':
        case '0': 
          digitalWrite(RELAY_PIN, LOW);
          break;
      }
    }
  }
  else
  {
    delay(100);
  }
}

CANZERO specific notes

As in most Arduino-compatible boards, the orange user LED is defined as LED_BUILTIN, e.g.:

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);

To send data on the serial interface (pin 13 and 14), you will need to refer to the Serial1 interface, e.g.:

Serial1.begin(9600);
Serial1.println("Hello world");