Pages: [1]   Go Down
Author Topic: 1 Wire DS18B20 Minimal Coding  (Read 1023 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm working on paring a sketch for space as I'm placing it on an ATTiny45, and so I've been deleting code left and right, replacing function calls with hard coded data.

Also... the data byte array in the example says it needs 9 bytes, but it only utilizes 2? I chopped it down to a 2 byte array and it worked, but I don't know if I'm somehow wrong for doing that.

One more thing... when I first plug in my sensor (doesn't happen when it's simply reset) I get a junk reading of 185.0f for my first reading. Not a big problem, but annoying.

I've come to the point where I'm not sure I can do any more. Help me chop!
Here is the getTemp function:
Code:
float getTemp(){
  resetDS();
  writeByte(0x44); // start conversion, without parasite power on at the end
 
  resetDS();
  writeByte(0xBE); // Read Scratchpad
 
  return (readByte()|(readByte() << 8))*0.1125+32;//convert to deg f
}

void resetDS()
{
  pinMode(DS18S20_Pin, OUTPUT);
  digitalWrite(DS18S20_Pin, LOW);
  delayMicroseconds(255);//480u reqd
  pinMode(DS18S20_Pin, INPUT);
  delayMicroseconds(255);//480u reqd
  writeByte(0xCC);//ROM skip as only one sensor
}

void writeByte(byte data){
  pinMode(DS18S20_Pin, OUTPUT);
  for (byte mask = 00000001; mask>0; mask <<= 1){
    if (data & mask ){ // send 1
      digitalWrite(DS18S20_Pin,LOW);
      delayMicroseconds(10);//15u reqd
      digitalWrite(DS18S20_Pin,HIGH);
      delayMicroseconds(45);//60u total
    }
    else{ //if bitwise and resolves to false
      digitalWrite(DS18S20_Pin,LOW); // send 0
      delayMicroseconds(50);
      digitalWrite(DS18S20_Pin,HIGH);
      //delayMicroseconds(5);//60u total is slow enough to negate needing this
    }
  }
}

byte readByte(){
  byte r = 0;
  for (byte mask = 00000001; mask>0; mask <<= 1){
    pinMode(DS18S20_Pin, OUTPUT);
    digitalWrite(DS18S20_Pin, LOW);
    //delayMicroseconds(3);//1u reqd pinMode() is slow enough to negate needing this
    pinMode(DS18S20_Pin, INPUT); // let pin float, pull up will raise
    delayMicroseconds(10);//15u reqd
    if(digitalRead(DS18S20_Pin))r|=mask;
    delayMicroseconds(40);//60u complete cycle
  }
  return r;
}

Here is the rest of the program...
Code:
#include <SPI.h>
#include <MyLiquidCrystal.h>
int DS18S20_Pin = 5; //DS18S20 thermometer pin*/

byte heatingStage=0;

const int q0 = 1;//first unused 74HC595 pin
const int q2 = 4;//second unused 74HC595 pin

LiquidCrystal lcd(0);// initialize LCD with the number of the ss/latch
     
void setup() { 
  lcd.begin(16, 2);// set up the LCD's number of columns and rows:
}
     
void loop() {
  noInterrupts();
  float temperature = getTemp();//temperature data
  interrupts();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temperature,1);
  lcd.print("f"); 
 
  if(temperature < 72 && heatingStage!=q0+q2){//stage two heating
    lcd._bitString=heatingStage=q0+q2;
    lcd.spiSendOut();
    lcd.setCursor(0,1);
    lcd.print("Stage 2 Heating ");
  }
  if(temperature < 78 && temperature > 72 && heatingStage!=q0){//stage one heating
    lcd._bitString=heatingStage=q0;
    lcd.spiSendOut();
    lcd.setCursor(0,1);
    lcd.print("Stage 1 Heating"); 
  } 
  if(temperature > 78){//heat off
    lcd._bitString=heatingStage=0;
    lcd.spiSendOut();
    lcd.setCursor(0,1);
    lcd.print("Heating Off    ");
  }
}
« Last Edit: June 09, 2013, 01:19:04 am by transcendtient » Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 180
Posts: 8108
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If there is only one device on the OneWire bus you can tell it to respond without requiring an address. That would eliminate the address array.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That did get me quite a bit, I appreciate it.
I've switched my code over to an SPI variant as the library code is smaller and easier to rummage through, and I'm exploring the possibility of adding an SD to log my temperature.
I'm still interested in any other size reductions anyone has.
Logged

Dee Why NSW
Offline Offline
Edison Member
*
Karma: 30
Posts: 1708
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Some your code alludes to the DS18S20. I don't know what the difference is, if any, but, if you are using just one DS18B20, I don't think it makes any sense to use the standard procedures for multiple devices.  You don't need any of that address stuff, and you don't need the one wire library.

You might find the code below useful. Strip out the stuff you don't need - which is most of it. It is actually written for two DS18B20s. Not a good idea, and the product of a comedy of errors.

Code:

#define tture1 14//no ; here
#define tture2 15//no ; here

// include the library codes:
#include <LiquidCrystal.h>
#include <SD.h>

File myFile;

// initialize the library with the numbers of MY interface pins
LiquidCrystal lcd(8,9,16,5,6,7);

int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

void setup() {
    //For each tture sensor: Do a pinMode and a digitalWrite
    pinMode(tture1, INPUT);
    pinMode(tture2, INPUT);

    digitalWrite(tture1, LOW);//Disable internal pull-up.
    digitalWrite(tture2, LOW);

    Serial.begin(9600);
    delay(300);//Wait for newly restarted system to stabilize
    Serial.print("Temperature measurement, two sensors:\n\n");

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  lcd.clear();
  // Print a message to the LCD.
  lcd.print("temp   in   out");   
 
  Serial.print("Initializing SD card...");
 
   pinMode(10, OUTPUT);
   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
 
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);//+++++++++++++++++++ OPEN
 
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing! Did you forget the date?");
    myFile.close();//++++++++++++++++++++++++++++++++++++++++++++ CLOSE

    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
 
}

void loop(){

// set the LCD cursor to column 4, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(4, 1);
  readTture(tture1);//N.B.: Values passed back in globals
  printTture();//N.B.: Takes values from globals. Also...
     //no newline part of printTture;

  delay(120);// Delay... must not be too short.
 
  // set LCD cursor for second temp
  lcd.setCursor(11, 1);
  readTture(tture2);//Now read and report 2nd tture.
  printTture();
  delay(200);// Delay... must not be too short.
  Serial.print("\n");//Start new line
 
       myFile = SD.open("test.txt", FILE_WRITE);//+++++++++++++++++++++++++OPEN
       myFile.println(",   ");
    myFile.close();//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>CLOSE
  delay(880);
}


void OneWireReset(int Pin) // reset.  Should improve to act as a presence pulse
{
     digitalWrite(Pin, LOW);
     pinMode(Pin, OUTPUT); // bring low for 500 us
     delayMicroseconds(500);
     pinMode(Pin, INPUT);
     delayMicroseconds(500);
}
//end OneWireReset

void OneWireOutByte(int Pin, byte d) // output byte d (least sig bit first).
{
   byte n;

   for(n=8; n!=0; n--)
   {
      if ((d & 0x01) == 1)  // test least sig bit
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(5);
         pinMode(Pin, INPUT);
         delayMicroseconds(60);
      }
      else
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(60);
         pinMode(Pin, INPUT);
      }
      d=d>>1; // now the next bit is in the least sig bit position.
   }
}//end OneWireOutByte

byte OneWireInByte(int Pin) // read byte, least sig byte first
{
    byte d, n, b;

    for (n=0; n<8; n++)
    {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(5);
        pinMode(Pin, INPUT);
        delayMicroseconds(5);
        b = digitalRead(Pin);
        delayMicroseconds(50);
        d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
    }
    return(d);
}//end OneWireInByte

void readTture(byte Pin){
//Pass WHICH pin you want to read in "Pin"
//Returns values in... (See global declarations)
  OneWireReset(Pin);
  OneWireOutByte(Pin, 0xcc);
  OneWireOutByte(Pin, 0x44); // perform temperature conversion, strong pullup for one sec

  OneWireReset(Pin);
  OneWireOutByte(Pin, 0xcc);
  OneWireOutByte(Pin, 0xbe);

  LowByte = OneWireInByte(Pin);
  HighByte = OneWireInByte(Pin);
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;
};//end readTture

void printTture(){//Uses values from global variables.
//See global declarations.
//N.B.: No new line inside printTture
      myFile = SD.open("test.txt", FILE_WRITE); //+++++++++++++OPEN
if (SignBit) // If it's negative
    {
     Serial.print("-");
     lcd.print("-");

     myFile.print("-");
    };
  Serial.print(Whole);
  Serial.print(".");
  lcd.print(Whole);
  lcd.print(".");
  myFile.print(Whole);
  myFile.print(".");
  if (Fract < 10)
    {
     Serial.print("0");
     lcd.print("0");
     myFile.print("0");
    };
  Serial.print(Fract);
  lcd.print(Fract);
  myFile.print(Fract);
    Serial.print("       ");
    myFile.print(",    ");
  myFile.close();//++++++++++++++++++++++++ CLOSE
};//end printTture

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have it down to the three functions
ds.reset()
ds.write()
ds.read()

I'm in the process of combing through those functions in the OneWire library to reduce their footprint.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

http://www.adafruit.com/datasheets/DS18B20.pdf is a great resource...

I recoded the functions I need and I'm still trying to squeeze anything at all out I can. Here is what I'm using as of now.

Code:
float getTemp(){
  resetDS();
  writeByte(0x44); // start conversion, without parasite power on at the end
 
  resetDS();
  writeByte(0xBE); // Read Scratchpad
 
  return (readByte()|(readByte() << 8))*0.1125+32;//convert to deg f
}

void resetDS()
{
  pinMode(DS18S20_Pin, OUTPUT);
  digitalWrite(DS18S20_Pin, LOW);
  delayMicroseconds(255);//480u reqd
  pinMode(DS18S20_Pin, INPUT);
  delayMicroseconds(255);//480u reqd
  writeByte(0xCC);//ROM skip as only one sensor
}

void writeByte(byte data){
  pinMode(DS18S20_Pin, OUTPUT);
  for (byte mask = 00000001; mask>0; mask <<= 1){
    if (data & mask ){ // send 1
      digitalWrite(DS18S20_Pin,LOW);
      delayMicroseconds(10);//15u reqd
      digitalWrite(DS18S20_Pin,HIGH);
      delayMicroseconds(45);//60u total
    }
    else{ //if bitwise and resolves to false
      digitalWrite(DS18S20_Pin,LOW); // send 0
      delayMicroseconds(50);
      digitalWrite(DS18S20_Pin,HIGH);
      //delayMicroseconds(5);//60u total is slow enough to negate needing this
    }
  }
}

byte readByte(){
  byte r = 0;
  for (byte mask = 00000001; mask>0; mask <<= 1){
    pinMode(DS18S20_Pin, OUTPUT);
    digitalWrite(DS18S20_Pin, LOW);
    //delayMicroseconds(3);//1u reqd pinMode() is slow enough to negate needing this
    pinMode(DS18S20_Pin, INPUT); // let pin float, pull up will raise
    delayMicroseconds(10);//15u reqd
    if(digitalRead(DS18S20_Pin))r|=mask;
    delayMicroseconds(40);//60u complete cycle
  }
  return r;
}
« Last Edit: June 09, 2013, 01:19:33 am by transcendtient » Logged

Melbourne, Australia
Offline Offline
God Member
*****
Karma: 8
Posts: 567
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

What is it compiling to, and how small does it need to be?
Logged

Windows serial port monitor: Tellurium | Arduino serial port debugging library: DBG | Cusom LCD char generator | Technical questions will only be answered in forum threads

Melbourne, Australia
Offline Offline
God Member
*****
Karma: 8
Posts: 567
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not a lot of help, however (compiled on a Mega, code untested, of course):

this saved 2 bytes:

Code:
//    if (digitalRead(DS18S20_Pin)) r |= mask;
r |= (mask & (digitalRead(DS18S20_Pin)));

This saved 8:

Code:
void writeByte(byte data){
  pinMode(DS18S20_Pin, OUTPUT);
  for (byte mask = 00000001; mask>0; mask <<= 1){
    digitalWrite(DS18S20_Pin,LOW);
    if (data & mask ){ // send 1
//    digitalWrite(DS18S20_Pin,LOW);
      delayMicroseconds(10);//15u reqd
      digitalWrite(DS18S20_Pin,HIGH);
      delayMicroseconds(45);//60u total
    }
    else{ //if bitwise and resolves to false
//    digitalWrite(DS18S20_Pin,LOW);
      delayMicroseconds(50);
      digitalWrite(DS18S20_Pin,HIGH);
      //delayMicroseconds(5);//60u total is slow enough to negate needing this
    }
  }
}

and I noticed in your first main sketch you had

Code:
//int DS18S20_Pin = 5; //DS18S20 thermometer pin
const int DS18S20_Pin = 5; //DS18S20 thermometer pin

which saved 24 bytes.
Logged

Windows serial port monitor: Tellurium | Arduino serial port debugging library: DBG | Cusom LCD char generator | Technical questions will only be answered in forum threads

Melbourne, Australia
Offline Offline
God Member
*****
Karma: 8
Posts: 567
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Another paltry 32 bytes (on the Mega 2560) but also catches the logic error if the the temperature == (78 || 72) smiley-wink

Code:
void loop() {
  noInterrupts();
  float temperature = getTemp();//temperature data
  interrupts();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temperature,1);
  lcd.print("f"); 
 
  if(temperature > 78){//heat off
    setStage(0,     "Heating Off    ");
  else if(temperature < 72) {
    if (heatingStage!=q0+q2) //stage two heating
      setStage(q0+q2, "Stage 2 Heating ");
  }
  else if (heatingStage!=q0){//stage one heating
    setStage(q0, "Stage 1 Heating ");
  } 
}

void setStage(byte thisValue, char *thisMessage) {
    lcd._bitString=heatingStage=thisValue;
    lcd.spiSendOut();
    lcd.setCursor(0,1);
    lcd.print(thisMessage);
}
Logged

Windows serial port monitor: Tellurium | Arduino serial port debugging library: DBG | Cusom LCD char generator | Technical questions will only be answered in forum threads

Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

A paltry 32 bytes is the kind of thing I need. I've changed all my int's to unsigned ints... I'm tearing every library function I'm using to pieces...

I'm down to 5.2k, and I had dreams of putting this on an ATTiny45, but I'm afraid that's probably not possible.

I need to get another 1.2k to fit it, but I'll probably be switching to the ATTiny85.
Logged

Melbourne, Australia
Offline Offline
God Member
*****
Karma: 8
Posts: 567
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The main lesson I learnt in playing researching and experimenting with this was:

inline code is cheaper than a single function call
more than one call to the same function is cheaper than inline code
consts are cheap!
sometimes really crazy things happen that make you think What the farrrkkk?

eg:

This code snippet was me trying to get fancy with the mask loop:

Code:
byte pauseArray[] = {50, 10};

void loop() {
    writeByte(17);
};


void writeByte(byte data){
    pinMode(DS18S20_Pin, OUTPUT);
 
  for (byte mask = 00000001; mask>0; mask <<= 1){
    digitalWrite(DS18S20_Pin,LOW);
    delayMicroseconds(pauseArray[data & mask]);
    digitalWrite(DS18S20_Pin,HIGH);
    delayMicroseconds(pauseArray[!(data & mask)]);
  }
}

Now compare it to this code, which was actually 2 bytes bigger:
Code:
byte pauseArray[] = {50, 10};


void loop() {
    writeByte(17);
};


void writeByte(byte data){
    pinMode(DS18S20_Pin, OUTPUT);
 
  for (byte mask = 00000001; mask>0; mask <<= 1){
    digitalWrite(DS18S20_Pin,LOW);
    delayMicroseconds(pauseArray[!(data & mask)]);
    digitalWrite(DS18S20_Pin,HIGH);
    delayMicroseconds(pauseArray[data & mask]);
  }
}

Both segments were ~40 bytes bigger than the simplified if/else  I posted further upthread.

I'm looking at getting some ATTiny iCs in soon, so wanting to try my hand at compressing code, this was a good intro.
Logged

Windows serial port monitor: Tellurium | Arduino serial port debugging library: DBG | Cusom LCD char generator | Technical questions will only be answered in forum threads

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 180
Posts: 8108
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

this saved 2 bytes:
Code:
//    if (digitalRead(DS18S20_Pin)) r |= mask;
r |= (mask & (digitalRead(DS18S20_Pin)));

That will only work correctly when mask == 1.  For all other values of mask (2, 4, 8, etc) the results of (mask & 1) is 0.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Offline Offline
Newbie
*
Karma: 0
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
That will only work correctly when mask == 1.  For all other values of mask (2, 4, 8, etc) the results of (mask & 1) is 0.

Yeah I noticed that. Can anyone recommend a very small SD library?

BTW here's my devastated new LCD library.

If you notice I've been trying to use SPI to replace write4Bits. If anyone has any suggestions for how to do this... smiley-grin

Code:
#include "MyLiquidCrystal.h"
#include "Arduino.h"

LiquidCrystal::LiquidCrystal(){
  SPI.begin();
  initSPI();
  init();  
  begin();
}

void LiquidCrystal::initSPI(){
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
}

void LiquidCrystal::init(){
  _rs_pin = 1;
  _rw_pin = 255;
  _enable_pin = 3;  
  _data_pins[0] = 4;
  _data_pins[1] = 5;
  _data_pins[2] = 6;
  _data_pins[3] = 7;

  pinMode(_rs_pin, OUTPUT);
  pinMode(_enable_pin, OUTPUT);  
}

void LiquidCrystal::begin() {
  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
  delayMicroseconds(50000);
  // Now we pull both RS and R/W low to begin commands
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_enable_pin, LOW);
  
  //put the LCD into 4 bit mode
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
/*_bitString=3;
    spiSendOut();
pulseEnable();*/
write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms
    
    // third go!
    write4bits(0x03);
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02);

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | LCD_2LINE);  

  // turn the display then clear
  command(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
  command(LCD_CLEARDISPLAY);
}

/********** high level commands, for the user! */

void LiquidCrystal::home()
{
  command(LCD_SETDDRAMADDR);
}

/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
  send(value, LOW);
}

inline size_t LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
    bitWrite(_bitString, _rs_pin, mode); //set RS to mode
    spiSendOut();
    
//we are not using RW with SPI so we are not even bothering
//or 8BITMODE so we go straight to write4bits
    write4bits(value>>4);
    write4bits(value);    
}

void LiquidCrystal::pulseEnable(void) {
    bitWrite(_bitString, _enable_pin, LOW);
    spiSendOut();
delayMicroseconds(1);
bitWrite(_bitString, _enable_pin, HIGH);
    spiSendOut();
delayMicroseconds(1);    // enable pulse must be >450ns
bitWrite(_bitString, _enable_pin, LOW);
    spiSendOut();
delayMicroseconds(40);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(byte value) {
    for (byte i = 4; i < 8; i++)
{
 //we put the four bits in the _bit_string
 bitWrite(_bitString, i, ((value >> (i - 4)) & 0x01));
}
//and send it out
spiSendOut();
  pulseEnable();
}

void LiquidCrystal::spiSendOut() //SPI #############################
{
  initSPI();
  
  digitalWrite(_latchPin, LOW);
  SPI.transfer(_bitString);
  digitalWrite(_latchPin, HIGH);
}
« Last Edit: June 09, 2013, 04:02:36 pm by transcendtient » Logged

Pages: [1]   Go Up
Jump to: