Chapter 8. Other Features and Capabilities

8.1. Revision Control

Dno projects are simply directory systems. As such they are compatible with all known, sane, revision control systems. Git in particular, works just fine.

8.2. Configuration Options

Each Arduino board has a number of user-selectable configuration options. For some boards, like the esp32, there can be many, many options.

To view and modify the options for your selected Arduino board, use dno menu. This presents the user with a menu-based interface for selecting from the available options.

The selected options become compiler flags for the next build. They are recorded in a file called BOARD_OPTIONS, which becomes a dependency for all object files in the build. This means that changing an option will cause the next build to recompile everything.

8.3. .ino files

Typically, Arduino sketches are presented as .ino files. They need to contain only the functions setup() and loop().

Dno, naturally, supports the .ino file format but it does not require it.

If you would rather code your own main() function, you can. There does not have to be a .ino file in your Arduino program.

8.4. Tags

Tag files are reference files that tell your favourite editor where various symbols can be found in your source code. They enable you to rapidly move to places of interest in your code.

There are 2 major flavours of tag files: ctags and etags. If you have a need for tagfiles, you will know which you prefer.

Dno creates tagfiles using the targets: ctags and etags. These targets should be invoked in a code directory: either a board directory, or the project directory if you are not using board directories.

The ctags target will create a file called tags, while the etags target creates a file called TAGS.

Once these files have been created, dno will automatically keep them up to date. Note that the clean target will leave any tags files in place, while the pristine target will remove them.

8.5. Serial Communication

You can monitor and interact with your Arduino's serial connection using dno monitor.

This creates a connection to your Arduino using the Linux screen command.

You should read the screen man page if you are unfamiliar with screen.

8.5.1. Getting Out of screen

If you are unfamiliar with screen you will quickly find yourself wondering, "how do I get out of this"?

If you find yourself in the monitor and want to get out, use C-a k. That is Control-A followed by k.

Another way to kill the screen program is by running dno noscreen from another session.

8.5.2. Why screen?

The screen utility provides multiplexed virtual terminals. This allows you to run multiple sessions from a single terminal.

By using screen as our monitor tool, we allow the user to switch between a command line session and the connection to the Arduino serial interface. This seemed like a good idea.

8.6. EEPROMs

Dno provides limited support for writing to an Arduino's EEPROM.

The reason that it is limited is that the platform.txt file (and the Arduino platform specification) does not provide support.

For AVR-architecture boards (the traditional Arduinos), dno provides the eeprom target, which will attempt to burn an eeprom image to a connected Arduino. Use this in much the same way as you use the upload target.

A typical use-case for this would be to give each Arduino in a connected cluster its own unique identifier or key. This would mean the same image could be uploaded to each member of the cluster, but a separate eeprom image provided for the individual members.

8.6.1. Coding For EEPROMs

There is much online documentation about using Arduino EEPROMs. Little of it seems to cover how to create eeprom images for directly writing to a board.

We'll take our blink sketch as the basis for the example below.

8.6.1.1. Include the eeprom Header

Add #include <avr/eeprom.h>. Something like this:

. . . 

#include <Deferal.h>
#include <avr/eeprom.h>

. . . 
	  

8.6.1.2. Create a Struct Representing the EEPROM Contents

We add a struct definition, with some initialisation. Note the use of the EEMEM directive. This is the crucial element, and for more information on using Arduino EEPROMs, this appears to be the best term to search for.

Our code now looks like this:

. . . 

#include <Deferal.h>
#include <avr/eeprom.h>

static bool led_is_on;
static Deferal myTimer(500);

#define SENTINEL 0xdeadbeef

struct {
    uint32_t sentinel;
    char my_nodename[40];
    uint16_t my_node_id;
} eeprom EEMEM = {
  	          SENTINEL,
		  "Here, put this fish in your ear",
		  42}; 
. . . 
	  

8.6.1.3. Using the EEPROM Contents

Now, we need to make use of the contents. We will read the eeprom structure, and write the values we have found to our serial line. Let's do this on each blink.

To ensure that we are not reading uninitialised junk, we will test that the sentinel field contains our expected value. If not, then the EEPROM has not been written.

This is the final program. Note that it is poorly commented. Adding Doxygen comments is left as an exercise for the reader.

/*
  Blink

*/

#include <Deferal.h>
#include <avr/eeprom.h>
static bool led_is_on;
static Deferal myTimer(500);

#define SENTINEL 0xdeadbeef
#define SERIAL_BAUD_RATE 57600

struct {
    uint32_t sentinel;
    char my_msg[40];
    uint16_t my_node_id;
} eeprom EEMEM = {
  	          SENTINEL,
		  "Here, put this fish in your ear",
		  42}; 

static void
led_on(int led)
{
    digitalWrite(led, HIGH);
    led_is_on = true;
}

static void
led_off(int led)
{
    digitalWrite(led, LOW);
    led_is_on = false;
}

static void
toggle_led(int led)
{
    if (led_is_on) {
	led_off(led);
    }
    else {
	led_on(led);
    }
}

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);
    led_off(LED_BUILTIN);
    Serial.begin(SERIAL_BAUD_RATE);
}


void
send_msg()
{
    char c;
    if (eeprom_read_dword(&eeprom.sentinel) == SENTINEL) {
        Serial.print("Node ");
        Serial.print(eeprom_read_word(&eeprom.my_node_id));
        Serial.print(" says ");
	for (int i = 0;
	     c = eeprom_read_byte(&(eeprom.my_msg[i]));
	     i++) {
	    Serial.print(c);
	}
	Serial.println();
    }
    else {
        Serial.print("eeprom not initialised: ");
        Serial.println(eeprom.sentinel);
    }
}

void loop()
{
    if (!myTimer.running()) {
	toggle_led(LED_BUILTIN);
	myTimer.again();
	send_msg();
    }
    // We can add code to do all sorts of things here, without the
    // timing of our led blinks being affected.
    
}
	  

8.6.1.4. Build and Test

Let's compile and upload:

pro.8MHzatmega328$ dno
  C++  [..] blink.ino
  LD blink.ino.o Deferal.o
  OBJCOPY (hex) blink.elf
pro.8MHzatmega328$ dno upload
  Resetting device attached to (/dev/ttyUSB0)...
/usr/local/bin/dno do_upload
make[1]: Entering directory '.../blink/pro.8MHzatmega328'
 
    Uploading blink.hex to /dev/ttyUSB0

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "build/blink.hex"
avrdude: writing flash (2740 bytes):

Writing | ################################################## | 100% 1.41s

avrdude: 2740 bytes of flash written
avrdude: verifying flash memory against build/blink.hex:
avrdude: load data flash data from input file build/blink.hex:
avrdude: input file build/blink.hex contains 2740 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 1.19s

avrdude: verifying ...
avrdude: 2740 bytes of flash verified

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.

make[1]: Leaving directory '.../pro.8MHzatmega328'
pro.8MHzatmega328$ 
	  

Now, we connect out monitor using dno monitor. This is what we see:

�eeprom not initialised
eeprom not initialised
eeprom not initialised
eeprom not initialised
eeprom not initialised
eeprom not initialised
	  

So, it appears to work, though the eeprom is uninitialised.

8.6.1.5. Burn the Eeprom

Please note again that this operation is only supported under the AVR architecture. It may be possible to do something similar with other architectures but since the Arduino platform specification does not seem to offer it, its unlikely that dno will support it.

To burn the eeprom (for an AVR architecture Arduino board) we can simply use dno eeprom. However for demonstration and documentation purposes we will split this into two steps: dno eeprom_image and then dno eeprom.

The eeprom_image target creates the image that will be written to the board. The eeprom target writes the image.

pro.8MHzatmega328$ dno eeprom_image
  OBJCOPY (eep) blink.elf
pro.8MHzatmega328$ dno eeprom
  Resetting device attached to (/dev/ttyUSB0)...
/usr/local/bin/dno do_eeprom
make[1]: Entering directory '.../blink/pro.8MHzatmega328'
 
    writing eeprom from blink.eep using /dev/ttyUSB0

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file "build/blink.eep"
avrdude: writing eeprom (46 bytes):

Writing | ################################################## | 100% 0.38s

avrdude: 46 bytes of eeprom written
avrdude: verifying eeprom memory against build/blink.eep:
avrdude: load data eeprom data from input file build/blink.eep:
avrdude: input file build/blink.eep contains 46 bytes
avrdude: reading on-chip eeprom data:

Reading | ################################################## | 100% 0.38s

avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x001a
         0x22 != 0x20
avrdude: verification error; content mismatch

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.

make[1]: *** [/usr/local/bin/dno:1420: do_eeprom] Error 1
make[1]: Leaving directory '.../blink/pro.8MHzatmega328'
make: *** [/usr/local/bin/dno:1414: eeprom] Error 2
pro.8MHzatmega328$ 
	  

8.6.1.6. Are We There Yet?

Well, let's try the monitor again:

	    Node 554 says Here, put this fish in your ear
Node 554 says Here, put this fish in your ear
Node 554 says Here, put this fish in your ear
Node 554 says Here, put this fish in your ear
Node 42 says Here, put this fish in your ear
Node 554 says Here, put this fish in your ear
Node 554 says Here, put this fish in your ear
. . .
	  

Well, something is a bit off , but it has mostly worked. The node_id value is usually wrong but sometimes ok. Whether this is a hardware problem (tired eeprom), or something else is difficult to say [4] . Either way, writing to the eeprom achieved something at least.



[4] It turns out the eeprom in my board was faulty. Running with a different board solved the problem.