DesignSpark Electrical Logolinkedin
Menu Search
Ask a Question

Building a Spresense Powered Smart Security Device Part 5: Putting It All Together

This series of posts demonstrates how the advanced capabilities of the Sony Spresense can be put to use in creating a low power security device for asset monitoring and tracking.

In previous posts, we introduced the project and covered SDK setup, following which explored the Sensor Control Unit (SCU), GNSS and onboard SPI flash storage. In this post, we will now take a look at using the examples previously covered to create a new application that will serve as a simple proof-of-concept for our security device use case.

Theory of operation

The application will be invoked in much the same way as the gnss_pvtlog example, with a default option that writes log files, a second option to read, and a third to delete. However, this time we will only write when the SCU event detector has determined that a sensor reading has exceeded a predefined threshold. When this happens, along with the time and present location, we will also log details of the event.

SDK configuration

The quickest way to configure a new application is to copy an existing one and then use this as a starting point. The Spresense SDK developer guide suggests using the hello example, but since our program will be combining the functionality in the tilt and gnss_pvtlog examples, it makes more sense to use one of these. Assuming that we are already in the sdk sub-directory and we would like to call our new application asset_protect, we would start by entering:

$ cd examples

$ cp -r tilt asset_protect

Following which we then need to replace all occurrences of tilt in the Make, Make.defs and kconfig files, with the string corresponding to our application name, asset_protect.

Next, we rename the main source file accordingly:

$ cd asset_protect

$ mv tilt_main.c asset_protect_main.c

We then also need to update this source file, but rather than replace every occurrence of tilt, we just need to update one line towards the end of the file, changing:

int tilt_main(int argc, char *argv[])

To read instead:

int asset_protect_main(int argc, char *argv[])

And now we should have our new example application. To configure build for this we enter:

$ cd ..

$ tools/config.py -m

Following which we could select the application under the Examples menu:

However, we would also need to make sure that we enabled I2C, the sensor subsystem and driver for the BMI160, along with the GNSS subsystem and the NMEA parser. A much simpler way is to create a new defconfig, of the sort we used previously with the examples:

$ vi sdk/configs/examples/asset_protect-defconfig

(substitute vi for your favourite editor of choice)

Then enter:

CONFIG_CXD56_I2C0=y
CONFIG_CXD56_GNSS=y
CONFIG_GPSUTILS_CXD56NMEA_LIB=y
CONFIG_SENSORS=y
CONFIG_BMI160=y
CONFIG_BMI160_I2C=y
CONFIG_EXAMPLES_ASSET_PROTECT=y

This is basically an amalgamation of the defconfig files for the tilt and gnss_pvtlog examples, with, of course, a final line that configures for our new application instead of either of these.

To then configure the SDK with our new defconfig, we enter:

$ tools/config.py examples/asset_protect

To check that everything has gone according to plan so far, we can build the application and flash to our Spresense module, following which connect to the UART:

$ make

$ tools/flash.sh -c /dev/ttyUSB3 nuttx.spk

$ screen /dev/ttyUSB3 115200

If we enter help at the NuttShell prompt, we see that we have a new built-in application, which we could run provided that we have a BMI160 sensor attached.

At this point, it functions precisely the same as the tilt example, but we have a new distinct app configured in our build system, which has the drivers enabled to integrate the BMI160, SCU sensor processing, GNSS and flash storage.

Application code

Since we started with the tilt example, we would now need to take a look at the gnss_pvtlog example and making sure that from this we have the relevant:

  • includes
  • pre-processor definitions
  • private types
  • private data
  • functions

Some of these may be redundant and additions may also be required. We would also ideally make changes in various places, such that function names etc. are appropriate for our new application. Rather than go through each step taken blow-by-blow, we will instead proceed to take a look at just the key parts of the resulting program which integrates the capabilities of both examples.

In our pre-processor definitions section we now have:

#define ACC_DEVPATH      "/dev/accel0"
#define EVENT_SIGNAL              17
#define MY_GNSS_SIG0              19
#define FILE_NAME_LEN             256
#define MAX_LOG_COUNT             10

The last define, MAX_LOG_COUNT, is to configure how many events we would like to log. Setting this to a low number in development, so that after testing the event detection and write capability a few times, we can quickly drop back to the NuttShell prompt and the board is then ready for a new binary to be uploaded again. Otherwise, we would have to press the reset button.

In the private types section, we still have structs sample_s and cxd56_gnss_dms_s. However, we have also created a new structure:

struct asset_protect_s {
  struct cxd56_gnss_date_s         date;
  struct cxd56_gnss_time_s         time;
  double                           latitude;
  double                           longitude;
  uint32_t                         ts;         /* [in] sample timestamp */
  uint32_t                         type;       /* Event type (SCU_EV_RISE or SCU_EV_FALL) */
};

This is used to store log records that contain the GNSS date, time and coordinates, plus the event detector timestamp and event type.

In the private data section, we have variables used for GPS data and log entries, plus the SCU IIR filter coefficients.

We’ll skip functions which were present in the tilt and gnss_pvtlog examples, and instead focus on just the new ones.

We have a getpos() function which simply reads position and time data from GNSS, and following which stores this in the posdat variable.

A new create_log() function is added which initialises the aplogdat variable (struct asset_protect_s) with all zeroes. Following which the ts and type members are assigned timestamp and event type from the SCU event detector. If the GNSS receiver has valid data, the position and time are set also.

static int create_log(uint32_t ts, uint32_t type)
{
  aplogdat = emptyLog;

  aplogdat.ts = ts;
  aplogdat.type = type;

  if (posdat.receiver.pos_dataexist &&
      (posdat.receiver.pos_fixmode != CXD56_GNSS_PVT_POSFIX_INVALID))
    {
      aplogdat.date = posdat.receiver.date;
      aplogdat.time = posdat.receiver.time;
      aplogdat.latitude = posdat.receiver.latitude;
      aplogdat.longitude = posdat.receiver.longitude;
    }

  return 0;
}

A new print_log() function has been added which takes an asset_protect_s struct, formats appropriately and prints out.

The asset_protect_read and asset_protect_delete functions are from the gnss_pvtlog example, more or less as-is. They are used to read and print out, and delete, events that are logged to flash.

The asset_protect_start function is invoked by default and when the “S” or “s” argument (start) is passed. This function:

  1. Opens the GNSS device
  2. Sets the GNSS signal
  3. Starts positioning
  4. Opens the accelerometer device
  5. Sets the SCU FIFO
  6. Sets the sequencer sampling rate
  7. Sets the event detector signal
  8. Starts the sequencer
  9. Sets event detection
  10. Waits for the event detector signal

Upon receiving the event detector signal, get_pos() is called, following which create_log(), print_log() and finally writefile(). One file is created for each event, up to the maximum number set by MAX_LOG_COUNT. There is also some error handling and devices are closed before exiting.

The program main function is pretty much the same as that of gnss_pvtlog.

For complete C source, plus defconfig, etc., see the GitHub repository.

In operation

Executing with the default mode we see start-up messages printed out and following which, when the event detector is triggered by tilting the BMI160, details are printed out to the console and at the same time logged to SPI flash storage. Note that if the GNSS has not yet managed to get a position fix, the lat and long will both be set to 0, making it clear that this is the case (unless you do happen to be located on Null Island!) Note that we do also have a timestamp from the SCU.

If we invoke asset_protect again and now this time with the “r” argument, we read and print out the contents of each log file.

Finally, we can use the “d” argument to delete the logs.

Taking it further

With this simple prototype, we’ve not taken advantage of the GNSS subsystem PVT logging capability, and one potential improvement might be to use this to get the most recent position fix when we are unable to ascertain the current position. There is also a lot could be done to improve data/file management and we might want to have some form of configuration file, e.g. that allows us to set the event detector sensitivity. Plus also perhaps adding some sort of security features that would prevent just anyone from deleting log files.

Final thoughts

In this series of posts, we’ve seen how the powerful capabilities of the Spresense can be combined to create an application which not only reads sensors and gets position and date/time via GNSS, but does so in a manner which is highly energy-efficient thanks to advanced hardware features. We’ve also seen how easy it is to get up and running with the NuttX-based Spresense SDK, and then to use this, together with the provided examples, to prototype our own application.

Andrew Back

Open source (hardware and software!) advocate, Treasurer and Director of the Free and Open Source Silicon Foundation, organiser of Wuthering Bytes technology festival and founder of the Open Source Hardware User Group.

12 Feb 2020, 13:10