Monday, April 15, 2013

A Virtual Wire Digital Thermometer

In this post we will look into some details on one of the Virtual Wire Interface (VWI) example sketches; CosaVWItempsensor. The basic idea behind the example is to demonstrate how to use the VWI transmitter together with the Cosa 1-wire driver (OWI) for the DS18B20 Programmable Resolution Digital Thermometer. The sketch sends messages with the Thermometer 1-Wire identity, a sequence number, and the thermometer and power supply (battery) measurements.

Fig.1: CosaVWItempsensor (ATtiny85V/RF433TX/DS18B20)

The sketch is designed to run on an ATtiny85 with the internal 8 MHz clock and at 3.3 V. It can easily be modified to run on an Arduino (i.e. Board::D1 should be changed). First a snippet from the sketch that shows the sections that construct and sends the message:

VirtualWireCodec codec;
VWI::Transmitter tx(Board::D1, &codec);
const uint16_t SPEED = 4000;
...
void setup()
{
  ..
  // Start the Virtual Wire Interface/Transmitter
  VWI::begin(SPEED);
  tx.begin();
  ...
}
...
// Message from the device. Use one-wire identity as virtual wire identity
struct msg_t {
  uint8_t id[OWI::ROM_MAX];
  uint16_t nr;
  int16_t temperature;
  uint16_t voltage;
};
...
void loop()
{
  static uint16_t nr = 0;
  ...
  msg_t msg;
  ...
  memcpy(&msg.id, sensor->get_rom(), sizeof(msg.id));
  msg.nr = nr++;
  msg.temperature = sensor->get_temperature();
  msg.voltage = AnalogPin::bandgap(1100);

  // Enable wireless transmitter and send. Wait completion and disable
  VWI::enable();
  tx.send(&msg, sizeof(msg));
  tx.await();
  VWI::disable();
  ...
}


The message (struct msg_t) consists of an identity, a sequence number, the temperature and a battery reading. The sensor's 1-wire identity [64-bit] is used as the message identity. To reduce power the transmitter is only activated for the transmission. VWI::enable/disable are used.

The sections that handles the 1-Wire Thermometer device temperature measurements from the sensors looks like this:

// Connect to one-wire device; Assuming there are two sensors
OWI owi(Board::D2);
DS18B20 indoors(&owi);
DS18B20 outdoors(&owi);
...
void setup()
{
  ...
  // Connect to the temperature sensor
  indoors.connect(0);
  outdoors.connect(1);
  ...
}
...
void loop()
{
  ...
  static DS18B20* sensor = &indoors;
  ...
  // Make a conversion request
  sensor->convert_request();

  // Read the temperature and initiate the message
  sensor->read_scratchpad();
  memcpy(&msg.id, sensor->get_rom(), sizeof(msg.id));
  msg.nr = nr++;
  msg.temperature = sensor->get_temperature();
  msg.voltage = AnalogPin::bandgap(1100);
  ...
  sensor = (sensor == &indoors) ? &outdoors : &indoors;
  ...
}


The same design pattern as for VWI is used for the 1-Wire driver support. The statement OWI owi(Board::D2) creates an instance of the 1-Wire manager and connects it to digital pin D2. The sensors, indoors and outdoors, are instances of the DS18B20 device driver. They use the 1-Wire manager, owi. This allows several 1-Wire buses if needed.

The first step is to connect the instances to the physical devices on the 1-Wire. The device driver support several methods. The easiest is to use the DS18B20::connect() member function which will bind the indexed device, read the configuration (resolution and alarm setting) and the power supply mode. The Cosa 1-Wire driver supports parasite powering of devices. No application code changes are required to activate parasite power handling and the device driver will detect the device setting when issuing either connect() or read_power_supply(). The setup() of the devices could also contain setting of the resolution and alarm thresholds. The default resolution is 12 bits fixed-point number (4 bit fraction).

The second step is to issue a convert_request(). The sampling and conversion will be performed on the device and the value is available after 750 milli-seconds (at the default 12-bit resolution). The necessary delay is handled in the read_scratchpad() function using the Cosa Watchdog based delay function for low power handling. The read_scratchpad() function will wait for the device and read back the scratchpad structure with the temperature measurement.

The last step is to initiate the message with the temperature reading and the rest of the message fields. Please note that the loop() will toggle between the two sensors for each call. Below is the full list of member functions for the DS18B20 device driver. These correspond directly to the functions in the data sheet.


Fig.2: DS18B20 Member Functions
The sketch also contains power enable/disable of different hardware modules in the ATtiny85 to reduce power consumption.  Cosa supports battery monitoring with the AnalogPin static member function bandgap(). This function will return the power supply (Vcc) in milli-volt.

 Fig.3: CosaVWItempmonitor (RF433RX/Arduino Mini Pro)

Please see the monitor sketch CosaVWItempmonitor.ino for details on the receiver. Also note that there is no retransmission though dropped messages may be detected with the sequence numbering.

[Update 2015-08-22]
The new support library Cosa Domotica makes it really easy to develop RF433 based sensor nodes.  The library performs common operations such as battery status measurement, message sequencing, low power sleep modes, etc.

2 comments:

  1. Hi, Mikael
    I think you could post this tutorial on Adruino forum
    and AVR forum too.
    Guillaume.

    ReplyDelete
  2. Hi, Guillaume, thanks for your help with this. Your suggestions and testing has helped improved the 1-Wire driver and the battery monitoring.

    I have posted on the Arduino forum but not the AVR forum. Might need to consider that. Also I am reusing the same posting for the notice of new blog posting, http://arduino.cc/forum/index.php?topic=150299.60#lastPost

    I might need to consider a separate item for each.

    Cheers!

    ReplyDelete