Notes About Working with Various Arduino & Netduino Microcontroller Boards

Friday, May 30, 2014

Reading Temperature from Multiple 1-Wire DS18B20 Sensors via a um-FPU64 Math Coprocessor

The um-FPU64 math coprocessor includes interfaces for connecting to many different kinds of sensors via its DEVIO functions. The free IDE that comes with the um-FPU64 includes examples of us er-defined routines stored in the coprocessor that  can be called from from an Arduino sketch, but I wanted to try to get it to work just from the "Arduino side" (without using the um-FPU64 IDE to program routines).  This example is really just exploration.  An Arduino can very handily read DS18B20s without help.  This example is really more about trying out the DEVIO instructions sent to the coprocessor using the Arduino FPU64 library. The code below reads the temperature from multiple DS18B20s, converts the reading to Celsius and Fahrenheit. It then calculates the average of the readings using the FPU64's matrix functions.  The average temperature in Celsius and Fahrenheit is then displayed on a Sainsmart I2C LCD display.  The Arduino drives the display because I haven't been able to get the um-FPU64's DEVIO LCD and DEVIO I2C functions to work with this display.

The um-FPU64 is available from Solarbotics. You can find documentation for the um-FPU64 on the MicroMega Corp. Web site.

The Arduino FPU64 library is available here on the Arduino Playground.

See my previous post on the SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD Module Shield for a quick introduction and link to the relevant library.

Connections


The um-FPU64 is connected to the Arduino Uno via SPI.  See this diagram on the Arduino Playground for connection information.  It is also possible to connect the coprocessor to the Arduion via I2C, though that is a slower connection.  See my earlier post, Connecting an uM-FPU64 Math Co-Processor to an Arduino Due via I2C, for more information.

The DS18B20s are hooked up for non-parasitic power.  Looking at the flat side of the sensor's head, the left pin is connected to 3V3, the center pin is connected to the center pin of the next sensor, and the right pin is connected to GND.  There is a 4.7k Ohm pull-up resistor on the connection to the FPU's D0 pin.

Code


The Fpu.write() instruction can send up to 6 FPU instructions and parameters in a single call.  This code tries to be efficient by sending multiple commands and parameters in each call, where this is possible.  Note that grouping multiple DEVIO calls in one Fpu.write() does not work or produces odd results. 

#include <Fpu64.h>
#include <SPI.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>


// ---> Change to number of DS1820B devices <--- 
#define DEV_CNT   5

//--- uM-FPU Register Definitions ---
#define RAW_TEMP  15   
#define AVG_TEMP  16
#define TEMP_C    20 //Start of group of registers to hold temp C data for each device 
#define TEMP_F    30 //Start of group of registers to hold temp F data for each device
#define AVG_C     40
#define AVG_F     50
#define DEV_CNT   60
#define DEV_ADDR  130 //Start of group of 128bit registers to hold 1-wire addr data for each device 

//--- OneWire Command Definitions --
#define OWIRE               0x10
#define ENABLE              0x01
#define RESET               0x02
#define SEARCH              0x05
#define SELECT              0x03
#define WRITE_BYTE          0x20
#define CONVERT_T           0x44
#define READ_SCRATCH_PAD    0xBE
#define READ_REG16_LSB_SE   0x3D

// Definition of matrix average operation not included in FPU64.h
#define AVE                 0x16

#define FPU64_PIN_D0        0x00

//Addr: 0x3F, 20 chars & 4 lines
LiquidCrystal_I2C lcd(0x3F, 20, 4); 

// Buffer for string representation of temperature to send to LCD
char text[10];

void setup() {
  Fpu.begin(); 
  delay(1000);
  Fpu.reset(); 
  lcd.init(); 
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Please wait...");
}  

void loop() { 
  // Start 1-wire bus connected to um-FPU64 pin D0  
  Fpu.write(DEVIO, OWIRE, ENABLE, FPU64_PIN_D0, 0x00);          
  Fpu.write(DEVIO, OWIRE, RESET);
  // Find 1-wire devices on bus
  // Device addresses stored starting at reg DEV_ADDR (128 bit)
  // Device addresses stored starting with least significant byte
  Fpu.write(DEVIO, OWIRE, SEARCH, DEV_CNT, DEV_ADDR);    
  // Cycle though devices, get ID & temp reported by each
  for(int i = 0; i < DEV_CNT; i++) {
    // Need to reset bus each time a device is accessed
    Fpu.write(DEVIO, OWIRE, RESET);  
    // Select device to communicate with
    Fpu.write(DEVIO, OWIRE, SELECT, DEV_ADDR+i);
    // Command device to take temp reading & store
    // in device's scratchpad
    Fpu.write(DEVIO, OWIRE, WRITE_BYTE, CONVERT_T);
    // Wait for reading to complete
    delay(750);
    Fpu.write(DEVIO, OWIRE, RESET);
    Fpu.write(DEVIO, OWIRE, SELECT, DEV_ADDR+i);
    // Send command to read raw temp data from device's scratch pad
    Fpu.write(DEVIO, OWIRE, WRITE_BYTE, READ_SCRATCH_PAD);
    // Now load raw data into FPU's RAW_TEMP register for calculation    
    // Read 2 bytes starting with least significant byte (LSB)
    // This value is a long integer
    Fpu.write(DEVIO, OWIRE, READ_REG16_LSB_SE, RAW_TEMP);
    // Use the FPU TEMP_C register corresponding to device as accumulator.
    // Load value from RAW_TEMP register
    // Convert it to a float & divide by 16 to get Celsius
    Fpu.write(SELECTA, TEMP_C+i, LSET, RAW_TEMP, F_FLOAT, FDIVI, 16); 
    //FpuSerial.printFloat(84);
    //Serial.print(" C, ");
    // Convert to Fahrenheit in corresponding register
    Fpu.write(SELECTA, TEMP_F+i, FSET, TEMP_C+i, FMULI, 9);
    Fpu.write(FDIVI, 5, FADDI, 32);
    // If last device, calculate and print averages using FPU64's matrix functions 
    if(i == DEV_CNT-1) {
      //Serial.print("\n>>> AVG: ");
      // Define a 1-column matrix using registers with TEMP_C values
      Fpu.write(SELECTMA, TEMP_C, DEV_CNT, 1);
      // Matrix average function puts average in reg 0 -
      // move average to reg AVG_TEMP so that it is not overwritten in reg 0
      // Convert float to ASCII using 9 digit format with 5 decimal places
      Fpu.write(MOP, AVE, SELECTA, AVG_TEMP, FSET0, FTOA, 95);
      // Clear buffer for string representation of temperature
      memset(text, 0x0, sizeof text);
      Fpu.readString(text);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(text);
      lcd.print(" C");
      // Repeat with matrix of Fahrenheit temps
      Fpu.write(SELECTMA, TEMP_F, DEV_CNT, 1);
      Fpu.write(MOP, AVE, SELECTA, AVG_TEMP, FSET0, FTOA, 95);
      memset(text, 0x20, sizeof text);
      Fpu.readString(text);
      lcd.setCursor(0, 1);
      lcd.print(text);
      lcd.print(" F");
    }
  }
}

No comments:

Post a Comment