Notes About Working with Various Arduino & Netduino Microcontroller Boards

Friday, March 29, 2013

Measuring Negative Temperatures with a OneWire DS18B20 & a Netduino


When working with temperatures below 0° C, the OneWire DS18B20 temperature sensor returns values that at first glance seem nonsensical. Below are some notes for Netduino (in C#) to deal with this issue.

The DS18B20 returns temperatures as 12-bit values, with the most significant bit being the sign bit. The remaining (highest or leftmost) 4 bits of the most significant byte are set to match the sign bit and can be ignored.

When the sign bit is 0, the remaining bits represent the (positive) temperature measured in sixteenths of a degree Celsius. When the sign bit is 1, the value represents a twos complement that encodes a negative value (again, in sixteenths of a degree Celsius).

All of this is discussed on pages 3 and 4 of the datasheet.

I posted a while back about using a single DS18B20 with a Netduino and  using multiple DS18B20s with a Netduino with the help of godfroi's OneWireBus and DS18B20 classes. To obtain correct readings when the temperature is below 0° C, a small modification of the DS18B20 class is required.

Change the lines that read

return (float)data / 16f; 

to this group of lines:


if (data < 2048) {
    return (float)data / 16f;
}
else
{
    return (float)-(~(data - 1) / 16f);
}

If the value is 2047 (binary 0111 1111 1111) or less, the sign bit is zero, so the value is positive. Dividing by a floating point value of 16 will give the positive temperature.

If the raw reading is 2048 (binary 1000 0000 0000) or more, the value is a negative number encoded as a twos complement. To convert this value,

  1. Subtract 1
  2. Reverse each bit using the ~ (bitwise not) operator converting each 0 to 1 and each 1 to 0.
  3. Divide by 16 (as a floating point value).
  4. Make the resulting value negative.   



Thursday, March 28, 2013

Using Two 4x4 Universal 16 Key Keypads for Arduino with an Arduino Due

A couple days ago I posted about using a Sparkfun SX1509 16 Output I/O Expander Breakout to connect two 4 x 4 membrane keypads to an Arduino Leonardo or Uno. The breakout board helps by reducing the number of connections between the keypads and the Arduino. If you are working with an Arduino Due, you have 54 digital pins to work with, so connecting directly between the keypads and the Arduino Due is an option in many situations.

Connections


Since this example uses direct connections between the keypads and the Due's pins, there is no doubling up of connections to the same pins for the columns or rows. Two keypads could share connections to the same pins for either the rows or the columns, but not both.  The fact that no row or columns are shared results in the empty quadrants in the key mapping in the code below.

There is a ribbon with 8 wires running from the bottom of the keypad. With the keypad face up, the wires connect in sequence from left to right to Arduino digital pins 2 - 9. Don't use digital pins 0 and 1 on the Arduino Uno, since they are used for serial communication.


Keypad 1  Arduino Due
1         D21
2         D20
3         D19
4         D18
5         D17
6         D16
7         D15
8         D14


Keypad 2  Arduino Due

1         D2

2         D3
3         D4
4         D5
5         D6
6         D7
7         D8
8         D9

Code


The Arduino Keypad library is available from the Arduino Playground.

The following code simply prints the character corresponding to each key press to the serial console.

#include <Keypad.h>

const byte ROWS = 8; 
const byte COLS = 8; 
char keys[ROWS][COLS] = {
  {'1','2','3','A',' ',' ',' ',' '}, // Keypad 1 map
  {'4','5','6','B',' ',' ',' ',' '}, 
  {'7','8','9','C',' ',' ',' ',' '},
  {'*','0','#','D',' ',' ',' ',' '},
  {' ',' ',' ',' ','E','F','G','H'}, // Keypad 2 map
  {' ',' ',' ',' ','I','J','K','L'},
  {' ',' ',' ',' ','M','N','O','P'},
  {' ',' ',' ',' ','Q','R','S','T'}
};
byte rowPins[ROWS] = {21,20,19,18,2,3,4,5}; //connect to row pinouts 
byte colPins[COLS] = {17,16,15,14,6,7,8,9}; //connect to column pinouts

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  Serial.begin(9600);
}

void loop(){
  char key = keypad.getKey();

  if (key != NO_KEY){
    Serial.println(key);
  }
}

Tuesday, March 26, 2013

Using Multiple 4x4 Universal 16 Key Keypads for Arduino with an SX1509 16 Output I/O Expander Breakout

I posted on March 20th about using the Sparkfun SX1509 16 Output I/O Expander Breakout to connect a 4 x 4 membrane keypad to an Arduino (Leonardo). If a project needs more than 16 keys, it is very easy to add a second, third, or or fourth keypad. I have two keypads, so this example shows how to use two, but it is easy to extend.

Connections


SX1509   Arduino Leonardo
3.3V     3.3V
nINT     D7
SCL      SCL
SDA      SDA
nRST     D8
GND      GND


The SX1509 breakout board has built-in pull-up resistors for the I2C connection, so external pull-ups aren't needed.


With the keypad face up and the ribbon cable pointing downward the connections are numbered 1 to 8 from left to right.


SX1509   Keypad 1

0        1
1        2
2        3
3        4
8        5
9        6
10       7
11       8


SX1509   Keypad 2
4        1
5        2
6        3
7        4
8        5   -- Note 2 connections to same column
9        6   -- Note 2 connections to same column
10       7   -- Note 2 connections to same column
11       8   -- Note 2 connections to same column

Code


//////////////////////////////////
//// Global Variables  ///////////
//////////////////////////////////
// Here we'll define the number of rows and columns our keypad
//  matrix has. Each of these values can be between 1 and 8.
//  4x4 = 16 buttons
const byte numRows = 8;
const byte numCols = 4;
// This key map will be used to define what keypress is sent to
//  the computer when a key on the keypad is pressed.
char keyMap[numRows][numCols] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'},
  {'E','F','G','H'},
  {'I','J','K','L'},
  {'M','N','O','P'},
  {'Q','R','S','T'}};

// Create a new sx1509Class object
sx1509Class sx1509(SX1509_ADDRESS, resetPin, interruptPin);


In this case, the second keypad serves as the fifth through eighth rows, sharing the same columns as the first keypad (hence the shared connections to the column pins on the SX1509 breakout). The third keypad would be added as columns 5 through 8 to the first four rows. the key map has to have columns and rows that line up, so that unused values are set to a character, even if they aren't used.

Because I wanted to print the output to the serial console and not use it a keyboard input to my computer, I edited these lines in the setup() method.


  #ifdef HID_ENABLED
  Keyboard.begin();
  #else
  Serial.begin(9600);
  #endif

and in the loop() method

  #ifdef HID_ENABLED      
  Keyboard.write(keyMap[activeRow][activeColumn]);
  #else
  Serial.print(keyMap[activeRow][activeColumn]);
  #endif


Wednesday, March 20, 2013

Using the 4x4 Universal 16 Key Keypad for Arduino with an SX1509 16 Output I/O Expander Breakout

I've posted in the past about using the 4x4 universal 16 key keypad for Arduino and the I2C version of using the 4x4 universal 16 key keypad for Arduino. The SX1509 16 output I/O expander and breakout from Sparkfun provides another way to read input from this 4x4 membrane keypad. The SX1509 uses I2C for communication with the Arduino, plus 2 connections for the reset and interrupt lines.

There is an Arduino library for the SX1509 that includes a very nice example of using it for keypad/keyboard input with abundant comments.

Connections


SX1509   Arduino Leonardo
3.3V     3.3V
nINT     D7
SCL      SCL
SDA      SDA
nRST     D8
GND      GND

The SX1509 breakout board has built-in pull-up resistors for the I2C connection, so external pull-ups aren't needed.  

With the keypad face up and the ribbon cable pointing downward the connections are numbered 1 to 8 from left to right. The key repeat functionality is nicely done, even if it may not be as useful with a keypad as with a full keyboard. 

SX1509   Keypad
0        1
1        2
2        3
3        4
8        5
9        6
10       7
11       8


Code


Use the sx1509_keypad example code that comes with the library as a starting point. 

Edit the Global Variables section of the code so it reads like this -


//////////////////////////////////
//// Global Variables  ///////////
//////////////////////////////////
// Here we'll define the number of rows and columns our keypad
//  matrix has. Each of these values can be between 1 and 8.
//  4x4 = 16 buttons
const byte numRows = 4;
const byte numCols = 4;
// This key map will be used to define what keypress is sent to
//  the computer when a key on the keypad is pressed.
char keyMap[numRows][numCols] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}};

// Create a new sx1509Class object
sx1509Class sx1509(SX1509_ADDRESS, resetPin, interruptPin);

Because I wanted to print the output to the serial console and not use it a keyboard input to my computer, I edited these lines in the setup() method. 

   #ifdef HID_ENABLED
  Keyboard.begin();
  #else
  Serial.begin(9600);
  #endif


and in the loop() method


    #ifdef HID_ENABLED      
  Keyboard.write(keyMap[activeRow][activeColumn]);
  #else
  Serial.print(keyMap[activeRow][activeColumn]);
  #endif

Wednesday, March 13, 2013

Sending Data from an Arduino to a Raspberry Pi Using I2C

Here is an example of how to send digital information from an Arduino (Leonardo) to a Raspberry Pi. The Leonardo gathers data from an array of DS18B20 1-wire digital thermometers and sends the readings to the Raspberry Pi over an I2C connection.  The Raspberry Pi is the I2C master and the Leonardo acts as slave.  The Arduino polls the sensors in a loop, and the Raspberry Pi requests the data every 5 seconds. The Arduino code is written in C. I have have used Python 2 on the RPi.

Note: the order that the sensors are arranged in does not affect the order in which they are read, but they will always be read in the same order, so you can work out which reading in the sequence applies to which DS18B20 thermometer by warming one with a finger and seeing which reading increases. With the I2C data block limited to 32 bytes, sending the 8 byte device ID of each sensor takes up too much space.

Connections:


The 1-wire DS18B20s are connected in parasitic power mode. Looking at the flat side the sensors -

  • The left pin of the 1st sensor is connected to GND on the breadboard/Arduino.
  • The center pin on the 1st pin is connected to the Arduino digital pin 10 (with a 4k7 pull-up to 5V).
  •  Connect the left pin of each sensor to the right pin of the same sensor.
  • Connect the center pin of each sensor to the center pin of the next sensor. 
  • Connect the right pin of each sensor to the left pin of the next sensor.

Connect the SDA and SCL lines between the Arduino and the Raspberry Pi (SDA to SDA & SCL to SCL). Be sure to connect the Arduino's GND to GND on the RPi. Note that I am not using a level shifter to convert between 3V3 and 5V. After reading a number of posts online, I used a multimeter to check the voltage on the SDA and SCL lines coming from the Arduino Leonardo. The voltage was consistently 3.4 - 3.5 volts. 

The RPi has built-in pull-up resistors on SDA and SCL, so no external resistors are needed.  

Code:


Here is the Arduino C code. The Python code to be run on the RPi is posted on my Raspberry Pi blog. The portion of the code below that reads the temperature from the DS18B20s is based sample code that comes with the Arduino OneWire library.

I2C on the Raspberry Pi seems to be limited to 32 byte blocks of data. This example sends 4 temperature readings. Each reading is a 4-byte float, so the data fits in one block. I'm sure it's possible to send larger amounts of data, but I don't know how to do so (yet).

#include <Wire.h>

#include <OneWire.h>

const int DEV_CNT = 4, I2C_ADDR = 0x03;

OneWire ds(10);  // 1wire bus on pin 10
int j = 0;
float fahrenheit[DEV_CNT];
byte dev[DEV_CNT][8];

void setup(void) {
  Wire.begin(I2C_ADDR);
  Wire.onRequest(requestEvent); //Register i2c request handler 
}

void loop(void) {
  byte i, present = 0, type_s = 0, data[12], addr[8];
  if(!ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  } 
  // Read 8-byte device ID
  for( i = 0; i < 8; i++) { dev[j][i] = addr[i]; }
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);   // start conversion, parasite power on   
  delay(750);      
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);     // Read Scratchpad
  for ( i = 0; i < 9; i++) {  data[i] = ds.read(); }
  // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  fahrenheit[j] = (float) raw / 16.0 * 1.8 + 32.0;
  ++j %= DEV_CNT;  // Increment j but keep within range of device count
}

void requestEvent() // Handle i2c requests
{
  // Buffer to hold temp data, 6 bytes for each device
  byte buffer[DEV_CNT*4];
  // Populate buffer with temp. data
  for(int a = 0; a < DEV_CNT; a++) {
    byte* b = (byte*) &fahrenheit[a];
    // 4 bytes for each float
    for(int i = 0; i < 4; i++) {
      buffer[a*DEV_CNT+i] = b[i];
    }
  }
  // Send data over i2c connection
  Wire.write(buffer, DEV_CNT*4);
}




Saturday, March 9, 2013

Connecting a uM-FPU Math Co-Processor to an Arduino via I2C

Last November, I made a couple posts about using the Micromega uM-FPU (32-bit) floating point unit with an Arduino. In February I posted about using the 64-bit version with an Arduino Due via I2C (rather than SPI). This post covers using the 32-bit math co-processor (uM-FPU v3.1) with an Arduino connected via I2C.


Connections:


With notch on top of the chip pointing upward, the pins are numbered moving counterclockwise down the left side and back up the right side.

uM-FPU v3.1   Arduino Leonardo
1 (/MCLR)     5V
4 (CS)        5V
9 (SERIN)     GND 
11 (SCL)      SCL (with 4k7 pull-up resistor)
12 (SDA)      SDA (with 4k7 pull-up resistor) 
13 (VSS)      GND
14 (VDD)      5V
17 (AVSS)     GND
18 (AVDD)     5V


Code:   


The manufacturer, Micromega Corp., has good uM-FPU documentation on their site. There are also a couple code examples included with the FPU library. Note, however, that this library will not work with an I2C connections. The code below shows how to pass values and command and values to the FPU and retrieve the results.  

This example calculates the square roots of integers 2 through 32766 and displays the result in the serial console.

#include <Wire.h>

#define FPU_ADDR   0x64
#define LOADWORD   0x5B
#define FTOA       0x1F   // Convert float to ASCII
#define SELECTA    0x01   // Select register A
#define SQRT       0x41   // reg[A] = sqrt(reg[A])
#define READSTR    0xF2   // Read string from string buffer

#define SYNC       0xF0   // Get synchronization byte
#define SYNC_CHAR  0x5C   // sync character

#define RESET_REG  0x01
#define RESET_CMD  0x00
#define DATA_REG   0x00

void setup() {
  Serial.begin(115200);
  while(!Serial) { ; }
  Serial.println("START");
  Wire.begin();
  Serial.println("0");
  // Reset FPU by writing 0 to I2C register addr. 1
  Wire.beginTransmission(FPU_ADDR);
  Wire.write(RESET_REG);
  Wire.write(RESET_CMD);
  Wire.endTransmission();
  // Recommended delay after reset
  delayMicroseconds(15);
  Serial.println("1");
  // Check FPU communication
  Wire.beginTransmission(FPU_ADDR);
  Wire.write(DATA_REG);
  Wire.write(SYNC);
  Wire.endTransmission();
  delay(10);
  Wire.requestFrom(FPU_ADDR, 1);
  while(Wire.available())    
  { 
    char c = Wire.read();    
    if(!c == SYNC_CHAR) {
      Serial.println("uM-FPU not responding.");
      while(1); 
    }      
  }
}

void loop() {
  for(int i = 1; i < 32767; i++) {
    String answer = "Sqrt of ";
    (answer += i) += " is ";
    Wire.beginTransmission(FPU_ADDR);
    Wire.write(DATA_REG);
    // Use reister 0 as accumulator
    Wire.write(SELECTA);
    Wire.write(0); 
    Wire.write(LOADWORD);
    Wire.write(i >> 8);    // Load high byte
    Wire.write(i & 0xFF);  // Load low byte
    Wire.write(SQRT);
    Wire.write(FTOA);
    Wire.write(0x00);
    Wire.write(READSTR);
    Wire.endTransmission();
    Wire.requestFrom(FPU_ADDR, 8);
    while(Wire.available())    
    { 
      char c = Wire.read();    
      answer += c;         
    }
    Serial.println(answer);
  }
  delay(5000);
  Serial.println();
}