0.1.0 MCP3424

This commit is contained in:
Rob Tillaart 2024-09-16 19:14:07 +02:00
parent 7fb98199e6
commit f458e0690e
16 changed files with 976 additions and 0 deletions

View File

@ -0,0 +1,42 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040 # name:architecture:board
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
teensy41:
board: teensy:Teensy:teensy41
package: teensy:Teensy
gcc:
features:
defines:
- ARDUINO_TEENSY41
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
teensy:Teensy:
url: https://www.pjrc.com/teensy/package_teensy_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
platforms:
- uno
# - due
# - zero
# - leonardo
#- m4
- esp32
#- esp8266
# - mega2560
- rpipico
# - teensy41

4
libraries/MCP3424/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: RobTillaart
custom: "https://www.paypal.me/robtillaart"

View File

@ -0,0 +1,13 @@
name: Arduino-lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
compliance: strict

View File

@ -0,0 +1,17 @@
name: Arduino CI
on: [push, pull_request]
jobs:
runTest:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: |
gem install arduino_ci
arduino_ci.rb

View File

@ -0,0 +1,18 @@
name: JSON check
on:
push:
paths:
- '**.json'
pull_request:
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: json-syntax-check
uses: limitusus/json-syntax-check@v2
with:
pattern: "\\.json$"

View File

@ -0,0 +1,11 @@
# Change Log MCP3424
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.1.0] - 2024-09-15
- initial version

21
libraries/MCP3424/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024-2024 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,230 @@
//
// FILE: MCP3424.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Arduino library for 18 bit ADC I2C MCP3424 and compatibles.
// URL: https://github.com/RobTillaart/MCP3424
#include "MCP3424.h"
/////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTOR
//
MCP3424::MCP3424(uint8_t address, TwoWire *wire)
{
_address = address;
_wire = wire;
_maxChannels = 4; // hack to support other devices from the series.
_channel = 0;
_gain = 1;
_bits = 12;
_mode = 0; // CONTINUOUS
_config = 0x10;
}
bool MCP3424::begin()
{
return isConnected();
}
bool MCP3424::isConnected()
{
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}
uint8_t MCP3424::getAddress()
{
return _address;
}
uint8_t MCP3424::getMaxChannels()
{
return _maxChannels;
}
int32_t MCP3424::read()
{
// return 12345; // TEST CODE
return readRaw();
}
float MCP3424::readVolts()
{
// TODO
// pre calculate float _factor = 15.625e-6 * pow(2, (_bits - 12))/ _gain;
return read() * 15.625e-6 * pow(2, (_bits - 12))/ _gain;
}
float MCP3424::readMilliVolts()
{
return read() * 15.625e-3 * pow(2, (_bits - 12))/ _gain;
}
float MCP3424::readMicroVolts()
{
return read() * 15.625e0 * pow(2, (_bits - 12))/ _gain;
}
// TODO move to derived class with more than one channel?
bool MCP3424::setChannel(uint8_t channel)
{
if (channel >= _maxChannels)
{
return false;
}
if (_channel != channel)
{
_channel = channel;
_config &= 0x1F; // channel = 0
if (channel == 1) _config |= 0x20;
if (channel == 2) _config |= 0x40;
if (channel == 3) _config |= 0x60;
}
writeConfig();
return true;
}
// TODO move to derived class with more than one channel?
uint8_t MCP3424::getChannel()
{
return _channel;
}
bool MCP3424::setGain(uint8_t gain)
{
if ((gain != 1) && (gain != 2) && (gain != 4) && (gain != 8))
{
return false;
}
if (_gain != gain)
{
_gain = gain;
_config &= 0xFC; // gain == 1
if (_gain == 2) _config |= 0x01;
if (_gain == 4) _config |= 0x02;
if (_gain == 8) _config |= 0x03;
writeConfig();
}
return true;
}
uint8_t MCP3424::getGain()
{
return _gain;
}
bool MCP3424::setResolution(uint8_t bits)
{
if ((bits != 12) && (bits != 14) && (bits != 16) && (bits != 18))
{
return false;
}
if (_bits != bits)
{
_bits = bits;
_config &= 0xF3; // bits == 12
if (_bits == 14) _config |= 0x04;
if (_bits == 16) _config |= 0x08;
if (_bits == 18) _config |= 0x0C;
writeConfig();
}
return true;
}
uint8_t MCP3424::getResolution()
{
return _bits;
}
void MCP3424::setContinuousMode()
{
_config &= ~0x10;
writeConfig();
}
void MCP3424::setSingleShotMode()
{
_config |= 0x10;
writeConfig();
}
uint8_t MCP3424::getMode()
{
return (_config & 0x10);
}
/////////////////////////////////////////////////////////////////////////////
//
// PRIVATE
//
int MCP3424::writeConfig()
{
_wire->beginTransmission(_address);
_wire->write(_config);
return _wire->endTransmission();
}
// status bit/byte is not read.
int32_t MCP3424::readRaw()
{
int32_t rv = 0;
// not optimized compacted code.
if (_bits == 18)
{
if (_wire->requestFrom(_address, (uint8_t)3) != 3)
{
// _error = PCF8574_I2C_ERROR;
return 0;
}
rv += _wire->read();
rv <<= 8;
rv += _wire->read();
rv <<= 8;
rv += _wire->read();
// handle sign bit.
if (rv & 0x00020000) rv |= 0xFF000000;
return rv;
}
// 12, 14, 16 bit
if (_wire->requestFrom(_address, (uint8_t)2) != 2)
{
// _error = PCF8574_I2C_ERROR;
return 0;
}
rv += _wire->read();
rv <<= 8;
rv += _wire->read();
// handle sign bit not needed.
return rv;
}
// -- END OF FILE --

View File

@ -0,0 +1,70 @@
#pragma once
//
// FILE: MCP3424.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: Arduino library for 18 bit ADC I2C MCP3424 and compatibles.
// URL: https://github.com/RobTillaart/MCP3424
#include "Arduino.h"
#include "Wire.h"
#define MCP3424_LIB_VERSION (F("0.1.0"))
// TODO
// - ERROR HANDLING
// - READ STATUS
// - CACHE LAST READ
//
class MCP3424
{
public:
// CONSTRUCTOR
MCP3424(uint8_t address = 0x68, TwoWire *wire = &Wire);
bool begin();
bool isConnected();
uint8_t getAddress();
uint8_t getMaxChannels();
// GET DATA
int32_t read();
// wrappers
float readVolts();
float readMilliVolts();
float readMicroVolts();
// CONFIG
bool setChannel(uint8_t channel = 0);
uint8_t getChannel();
bool setGain(uint8_t gain = 1);
uint8_t getGain();
bool setResolution(uint8_t bits = 12);
uint8_t getResolution();
// MODE
void setContinuousMode(); // default
void setSingleShotMode();
uint8_t getMode();
private:
uint8_t _maxChannels;
uint8_t _channel;
uint8_t _gain;
uint8_t _bits;
uint8_t _mode;
uint8_t _config;
uint8_t _address;
TwoWire * _wire;
int writeConfig();
int32_t readRaw();
};
// -- END OF FILE --

187
libraries/MCP3424/README.md Normal file
View File

@ -0,0 +1,187 @@
[![Arduino CI](https://github.com/RobTillaart/MCP3424/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![Arduino-lint](https://github.com/RobTillaart/MCP3424/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MCP3424/actions/workflows/arduino-lint.yml)
[![JSON check](https://github.com/RobTillaart/MCP3424/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MCP3424/actions/workflows/jsoncheck.yml)
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/MCP3424.svg)](https://github.com/RobTillaart/MCP3424/issues)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MCP3424/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/MCP3424.svg?maxAge=3600)](https://github.com/RobTillaart/MCP3424/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/MCP3424.svg)](https://registry.platformio.org/libraries/robtillaart/MCP3424)
# MCP3424
Arduino library for 18 bit ADC I2C MCP3424 and compatibles.
## Description
**Experimental**
Library not tested with hardware yet.
This library is to be used to configure and read the 18 bit MCP4324 4 channel ADC et al.
The MCP3424 is not a fast ADC, however with 18 bit it has at least a very high
resolution. What the effects of the long sampling time means is to be investigated.
The high resolution combined with an optional gain of 8x means one could
measure voltage in steps of about 2 µV
The library has three functions that return a reading in volts, millivolts or
microvolts to match the need of the user.
The user has to configure the ADC device (bits, gain) and can call
**read()** (et al) without parameters to keep usage straightforward.
Current implementation will probably change slightly in the future
when related devices will be supported. (See future section).
Alt-230 = µ
### Resolution
| Bits | LSB (gain=1) | SPS | Raw range | Notes |
|:------:|---------------:|:------:|:-------------------:|:-------:|
| 12 | 1 mV | 240 | -2048 .. 2047 |
| 14 | 250 µV | 60 | -8192 .. 8191 |
| 16 | 62.5 µV | 15 | -32768 .. 32767 |
| 18 | 15.625 µV | 3.75 | -131072 .. 131071 | not for 3426/27/28.
The effective resolution also depends on the gain set.
In theory with a gain of 8 the LSB of the 18 bit resolution represents
1/8 of 15.625 µV == 1.95 µV.
If this is feasible in practice is to be seen.
### I2C Address
The MCP3421 has a fixed address 0x68, one can order different addresses at factory (how?).
TODO Table of addresses?
Max speed ? 400 KHz.
### I2C multiplexing
Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up
to eight channels (think of it as I2C subnets) which can use the complete
address range of the device.
Drawback of using a multiplexer is that it takes more administration in
your code e.g. which device is on which channel.
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices
too if they are behind the multiplexer.
- https://github.com/RobTillaart/TCA9548
### Related
- https://gammon.com.au/adc tutorial about ADC's (UNO specific)
- https://github.com/RobTillaart/ADS1x15 (12 & 16 bit ADC, I2C, slow)
- https://github.com/RobTillaart/PCF8591 (8 bit ADC + 1 bit DAC)
- https://github.com/RobTillaart/MCP_DAC SPI based ADC.
- https://github.com/RobTillaart/MCP3424 18 bit ADC, I2C
## Interface
```cpp
#include "MCP3424.h"
```
### Constructor
TODO other constructors.
- **MCP3424(uint8_t address = 0x68, TwoWire \*wire = &Wire)**
- **bool begin()** initializes the device. POR?
- **bool isConnected()** checks if the device address can be seen on I2C bus.
- **uint8_t getAddress()** idem, convenience function.
- **uint8_t getMaxChannels()** idem, convenience function.
### Read
- **int32_t read()** returns the raw reading.
- **float readVolts()** converts the raw reading to volts value (wrapper).
- **float readMilliVolts()** converts the raw reading to millivolts value (wrapper).
This is useful for small ranges.
- **float readMicroVolts()** converts the raw reading to microvolts value (wrapper).
This is useful for very small ranges (especially with a gain of 8 one has a
resolution of about 2 microvolts.
### Configuration
The user has to configure the ADC device (bits, gain) and can call
**read()** (et al) without parameters to keep usage straightforward.
Correct settings will be written to the device immediately, but be aware of the fact
that it will take some time before the conversion with new settings is done.
- **bool setChannel(uint8_t channel = 0)** not to be used for the MCP3421 as
it has only one channel. Default is channel 0, parameter should be less than the
value of **getMaxChannels()**.
- **uint8_t getChannel()** returns chosen channel, 0 based. e
- **bool setGain(uint8_t gain = 1)** set gain to 1,2,4, or 8.
Other values will return false and not change the setting.
- **uint8_t getGain()** returns the set gain (default 1).
- **bool setResolution(uint8_t bits = 12)** set the bit resolution 12,14,16 or 18.
Other values will return false and not change the setting.
- **uint8_t getResolution()** returns the set resolution.
- **void setContinuousMode()** idem.
- **void setSingleShotMode()** idem.
- **uint8_t getMode()** idem.
The set function write their changes directly to the device. It might be better
to have one function to set all parameters in one call. To be investigated.
The library caches the last configuration, it is not read back from the device.
## Future
#### Must
- get hardware to test.
- redo interface for MCP3424 if needed.
- investigate ready flag
- investigate continuous vs single shot.
- improve documentation
#### Should
- implement support for (separate classes ?)
- 18 bit, MCP3421/MCP3422/MCP3423
- 16 bit: MCP3426/MCP3427/MCP3428 (no 18 bit so not 100% compatible)
- test on different boards.
- optimize performance (a lot of same math in conversion to voltage)
- optimize setting all configuration in one function call.
- PowerOnReset function for configuration
#### Could
#### Wont
## Support
If you appreciate my libraries, you can support the development and maintenance.
Improve the quality of the libraries by providing issues and Pull Requests, or
donate through PayPal or GitHub sponsors.
Thank you,

View File

@ -0,0 +1,100 @@
//
// FILE: MCP3424_test.ino
// AUTHOR: Rob Tillaart
// PURPOSE: basic test API calls.
// URL: https://github.com/RobTillaart/MCP3424
//
// needs a device connected to be able to test.
//
#include "MCP3424.h"
MCP3424 mcp;
uint32_t start, stop;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("MCP3424_LIB_VERSION: ");
Serial.println(MCP3424_LIB_VERSION);
Serial.println();
Wire.begin();
Wire.setTimeout(10000);
mcp.begin();
Serial.println();
Serial.print("Address:\t");
Serial.println(mcp.getAddress(), HEX);
Serial.print("Connect:\t");
Serial.println(mcp.isConnected());
Serial.print("Channels:\t");
Serial.println(mcp.getMaxChannels());
Serial.print("Gain:\t");
Serial.println(mcp.getGain());
Serial.print("Bits:\t");
Serial.println(mcp.getResolution());
Serial.println();
delay(100);
for (uint32_t speed = 100000; speed <= 800000; speed += 100000)
{
Wire.setClock(speed);
Serial.print("\nI2C CLOCK: \t");
Serial.println(speed);
test();
}
Serial.println("\ndone...");
}
void loop()
{
}
void test()
{
// test gain
start = micros();
mcp.setGain(2);
stop = micros();
Serial.print("SETGAIN: \t");
Serial.println(stop - start);
delay(10);
// test read for different resolutions.
for (uint8_t res = 18; res >= 12; res -= 2)
{
start = micros();
mcp.setResolution(res);
stop = micros();
Serial.print("RES_");
Serial.print(res);
Serial.print(": \t");
Serial.println(stop - start);
delay(10);
start = micros();
mcp.read();
stop = micros();
Serial.print("READ: \t\t");
Serial.println(stop - start);
delay(10);
// milli and micro volts are identical code.
start = micros();
mcp.readVolts();
stop = micros();
Serial.print("VOLTS: \t\t");
Serial.println(stop - start);
delay(10);
}
}
// -- END OF FILE --

View File

@ -0,0 +1,82 @@
//
// FILE: MCP3424_test.ino
// AUTHOR: Rob Tillaart
// PURPOSE: basic test API calls.
// URL: https://github.com/RobTillaart/MCP3424
#include "MCP3424.h"
MCP3424 mcp;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println(__FILE__);
Serial.print("MCP3424_LIB_VERSION: ");
Serial.println(MCP3424_LIB_VERSION);
Serial.println();
Wire.begin();
Wire.setTimeout(10000);
mcp.begin();
Serial.println();
Serial.print("Address:\t");
Serial.println(mcp.getAddress(), HEX);
Serial.print("Connect:\t");
Serial.println(mcp.isConnected());
Serial.print("Channels:\t");
Serial.println(mcp.getMaxChannels());
Serial.print("Gain:\t");
Serial.println(mcp.getGain());
Serial.print("Bits:\t");
Serial.println(mcp.getResolution());
Serial.println();
Serial.println("GAIN 1");
mcp.setGain(1);
Serial.println(mcp.read());
Serial.println(mcp.readVolts(), 6);
Serial.println(mcp.readMilliVolts(), 3);
Serial.println(mcp.readMicroVolts(), 3);
Serial.println();
Serial.println("GAIN 2");
mcp.setGain(2);
Serial.println(mcp.read());
Serial.println(mcp.readVolts(), 6);
Serial.println(mcp.readMilliVolts(), 3);
Serial.println(mcp.readMicroVolts(), 3);
Serial.println();
Serial.println("GAIN 4");
mcp.setGain(4);
Serial.println(mcp.read());
Serial.println(mcp.readVolts(), 6);
Serial.println(mcp.readMilliVolts(), 3);
Serial.println(mcp.readMicroVolts(), 3);
Serial.println();
Serial.println("GAIN 8");
mcp.setGain(8);
Serial.println(mcp.read());
Serial.println(mcp.readVolts(), 6);
Serial.println(mcp.readMilliVolts(), 3);
Serial.println(mcp.readMicroVolts(), 3);
Serial.println();
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,39 @@
# Syntax Colouring Map For MCP3424
# Data types (KEYWORD1)
MCP3421 KEYWORD1
MCP3422 KEYWORD1
MCP3423 KEYWORD1
MCP3424 KEYWORD1
MCP3426 KEYWORD1
MCP3427 KEYWORD1
MCP3428 KEYWORD1
# Methods and Functions (KEYWORD2)
begin KEYWORD2
isConnected KEYWORD2
getAddress KEYWORD2
getMaxChannels KEYWORD2
read KEYWORD2
readVolts KEYWORD2
readMilliVolts KEYWORD2
readMicroVolts KEYWORD2
setChannel KEYWORD2
getChannel KEYWORD2
setGain KEYWORD2
getGain KEYWORD2
setResolution KEYWORD2
getResolution KEYWORD2
setContinuousMode KEYWORD2
setSingleShotMode KEYWORD2
getMode KEYWORD2
# Constants (LITERAL1)
MCP3424_LIB_VERSION LITERAL1

View File

@ -0,0 +1,23 @@
{
"name": "MCP3424",
"keywords": "",
"description": "Arduino library for 18 bit ADC I2C MCP3424 et al.",
"authors":
[
{
"name": "Rob Tillaart",
"email": "Rob.Tillaart@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/RobTillaart/MCP3424.git"
},
"version": "0.1.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",
"headers": "MCP3424.h"
}

View File

@ -0,0 +1,11 @@
name=MCP3424
version=0.1.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for 18 bit ADC I2C MCP3424 et al.
paragraph=
category=Sensors
url=https://github.com/RobTillaart/MCP3424
architectures=*
includes=MCP3424.h
depends=

View File

@ -0,0 +1,108 @@
//
// FILE: unit_test_001.cpp
// AUTHOR: Rob Tillaart
// DATE: 2024-09-15
// PURPOSE: unit tests for the MCP3424 sensor
// URL: https://github.com/RobTillaart/MCP3424
// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md
//
// supported assertions
// ----------------------------
// assertEqual(expected, actual)
// assertNotEqual(expected, actual)
// assertLess(expected, actual)
// assertMore(expected, actual)
// assertLessOrEqual(expected, actual)
// assertMoreOrEqual(expected, actual)
// assertTrue(actual)
// assertFalse(actual)
// assertNull(actual)
#include <ArduinoUnitTests.h>
#include "Arduino.h"
#include "MCP3424.h"
unittest_setup()
{
fprintf(stderr, "MCP3424_LIB_VERSION: %s\n", (char *) MCP3424_LIB_VERSION);
Wire.begin();
// Wire.setTimeout(10000);
}
unittest_teardown()
{
}
unittest(test_constants)
{
assertEqualFloat(1, 1, 0.001);
}
unittest(test_constructor)
{
MCP3424 mcp;
assertEqual(0, mcp.getChannel());
assertEqual(4, mcp.getMaxChannels());
assertEqual(1, mcp.getGain());
assertEqual(12, mcp.getResolution());
// mode
}
unittest(test_gain)
{
MCP3424 mcp;
assertEqual(1, mcp.getGain());
assertTrue(mcp.setGain(2));
assertEqual(2, mcp.getGain());
assertTrue(mcp.setGain(4));
assertEqual(4, mcp.getGain());
assertTrue(mcp.setGain(8));
assertEqual(8, mcp.getGain());
assertTrue(mcp.setGain());
assertEqual(1, mcp.getGain());
assertFalse(mcp.setGain(0));
}
unittest(test_resolution)
{
MCP3424 mcp;
assertEqual(12, mcp.getResolution());
assertTrue(mcp.setResolution(14));
assertEqual(14, mcp.getResolution());
assertTrue(mcp.setResolution(16));
assertEqual(16, mcp.getResolution());
assertTrue(mcp.setResolution(18));
assertEqual(18, mcp.getResolution());
assertTrue(mcp.setResolution());
assertEqual(12, mcp.getResolution());
assertFalse(mcp.setResolution(13));
}
unittest_main()
// -- END OF FILE --