Arduino 74HC595/Seven Segment Display - Leading zeros and negative numbers issue.

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
Greetings,
Recently, I attempted to build an Arduino based temperature probe using a DS18B20 thermal probe. The output is a multiplexed 74HC595 used to drive 4 seven segment displays. The build works but, it is not able to read negative temps. In the code, I was able to move the decimal point over to read only tenths (000.0) instead of including hundredths (00.00) but I haven't been able to successful remove the leading zeros. Attempts at using a mask bits have been unsuccessful. I have scoured Google for many moons trying to figure out how to correct this but examples don't seem to work and I am getting lost in the programming and I'm left going in circles. Obviously, expert programmer I am not. :) The DS18B20 has a range of -55°C to +125°C (-67°F to +257°F). Is there a sketch using seven segment displays like the schematic below that takes advantage of this wider range? I was using this as a starting point to build a deep freezer controller once I got past the negative temperatures and the leading zero issues but it's a no go. There are plenty of kegerators out there but I don't want to use an LCD. I chose to use seven segment displays in this configuration due to space constraints.

I built this using four separate displays instead of a single four digit because I don't have one on hand but the wiring concept is essentially the same. The sketch I used that worked for me was from the following:
http://www.pial.net/arduino-controlling-a-4-digit-seven-segment-display/

Below is the sketch I used from this site along with changes I've made:

*****
C:
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

const int ledPin =  13;// LED connected to digital pin 13
const int latchPin = 8;//Pin connected to ST_CP of 74HC595
const int clockPin = 9;//Pin connected to SH_CP of 74HC595
const int dataPin = 10;//Pin connected to DS of 74HC595

const int digitPins[4] = {
  3,4,5,6}; //pins to control the 4 common anode pins of the display

const byte digit[10] = //seven segment digit bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01001111, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111  //9
};

int digitBuffer[4] = {
  0};
int digitScan = 0;
int soft_scaler = 0;
float tempC, tempF;
int tmp;

void setup()  {
  TCCR2A = 0;
  TCCR2B = (1<<CS21);
  TIMSK2 = (1<<TOIE2);
  TCNT2 = 0;

  pinMode(ledPin, OUTPUT);
  for(int i=0;i<4;i++)
  {
  pinMode(digitPins[I],OUTPUT);
  }[/I]
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT); 

  sensors.begin();
  sensors.getAddress(insideThermometer, 0);
}

ISR(TIMER2_OVF_vect) {
  soft_scaler++;
  if(soft_scaler==15)
  {
  refreshDisplay();
  soft_scaler = 0;
  }
}; 

void refreshDisplay()
{
  for(byte k=0;k<4;k++)
  {
  digitalWrite(digitPins[k], LOW);
  }
  digitalWrite(latchPin, LOW); 
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(400);
  digitalWrite(digitPins[digitScan], HIGH);

  digitalWrite(latchPin, LOW); 
  if(digitScan==1) //changed from if(digitScan==2)
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B10000000)); //inserting the dot
  }
  else
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~digit[digitBuffer[digitScan]]);
  }
  digitalWrite(latchPin, HIGH);
  digitScan++;
  if(digitScan>3) digitScan=0;
}

void loop()  
{
  digitalWrite(ledPin, HIGH);
  sensors.requestTemperatures();
  tempC = sensors.getTempC(insideThermometer);
  tempF = DallasTemperature::toFahrenheit(tempC);
  tmp = int(tempF*100);
   digitBuffer[2] = tmp/1000;
     digitBuffer[1] = (tmp%1000)/100;
     digitBuffer[0] = (tmp%100)/10;

   //This is original code and was changed to the above to only show 1/10 decimal
  //digitBuffer[3] = tmp/1000;
  //digitBuffer[2] = (tmp%1000)/100;
  //digitBuffer[1] = (tmp%100)/10;
  //digitBuffer[0] = (tmp%100)%10;

  digitalWrite(ledPin, LOW);
  delay(500);  //changed from delay(50)
}
*****

I ran into another blog that that appears to have successfully done what I am looking for but the build is over complicated. Some of the project appears to be inconsistent with it's code and I find the code difficult to dissect to rewrite for a single sensor and single display. It can be found at:

http://mskvorc.com/blog/2012/05/arduino-temperature-sensor-box/


I have included the schematic of my build below. Any help would be most greatly appreciated. Many thanks in advance.

Arduino_deepfreezer_thermostat1-page1.png
Mod edit: code tags
 
Last edited by a moderator:

ErnieM

Joined Apr 24, 2011
7,992
In line 99 you convert your reading to an integer. Does the Arduino allow for negative integers? (I don't use them so I do not know, I am asking you to check that). I suspect You may be converting all readings there to positive numbers. If not...

You have 3 items in your buffer for 4 digits, one is for the sign? For a negative sign all you want to do is turn on the G segment. You can do that by testing if the Fahrenheit reading is less than zero, then forcing the reading to be positive, and then follow with your conversion code to change the binary data to seven segment codes.

I believe G would be 0b00010000, you can stick that as the next item on your convention table.
 

ErnieM

Joined Apr 24, 2011
7,992
You may also want to cut your problem into pieces, and fix each piece. Here that means don't read the thermometer, assign some dummy data and see it displays correctly. You would want to do this several times to check various numbers, something high positive, high negative, zero, and so on.

Leading zero suppression is a little trickier as it depends on the value you have. It can be the next step after you get the sign correct and you choose how to present this data: does the negative sign stay where it is or does it follow the data to the next spot over?
 

MMcLaren

Joined Feb 14, 2010
842
Have you researched the format of the temperature value returned from the library? The DS18B20 normally provides a signed integer Celsius*16 value (1/16 degree resolution) which you may want to adjust for your display. In my PIC based 4-Digit DS18B20 project, I used simple code (below) to provide °C*10 or °F*10 values for the display along with a negative flag to tell you when to display a '-' (minus) character instead of the degrees*100 character in the first digit of the display.

Hope this helps. Good luck with your project.

Cheerful regards, Mike

Code:
    /*                                                                *
     *  the 16C term in the formulas below is the raw Celsius * 16    *
     *  temperature data from the DS18B20 in the 'temp16' variable.   *
     *  a value of °C*10 or °F*10 is used for the display with a      *
     *  decimal point inserted before the last (tenths) digit.        *
     *                                                                *
     *  10C = 8C + 2C = 16C/2 + (16C+4)/8                             *
     *                                                                *
     *  10F = 18C + 320 = 16C + 2C + 320 = 16C + (16C+4)/8 + 320      *
     *                                                                */
       bin = temp16;            // calculate common '2C' term first
       bin += 4;                // rounding
       bin /= 8;                // now bin = '2C' (with rounding)
       if(flags.7)              // if <right arrow> key --> Fahrenheit
       { bin += temp16;         // now bin = 16C + 2C
         bin += 320;            // now bin = 16C + 2C + 320 = 10F
       }                        //
       else                     // not <right arrow> key --> Celsius
       { temp16 /= 2;           // temp16 = 8C
         bin += temp16;         // now bin = 8C + 2C = 10C
       }                        //
       if(negflag = bin.15)     // set/clear "negflag", if negative
         bin = -bin;            // twos complement the temperature
 
Last edited:

djsfantasi

Joined Apr 11, 2010
5,571
Just a quick bit of information. Signed integers are amongst the data types supported by an Arduino.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
In line 99 you convert your reading to an integer. Does the Arduino allow for negative integers? (I don't use them so I do not know, I am asking you to check that). I suspect You may be converting all readings there to positive numbers. If not...
The sketch in my first post does not. Reading negatives will return a jumbled display due to the lack of the 2's complement routine being placed in.. The second sketch (found in http://mskvorc.com/blog/2012/05/arduino-temperature-sensor-box/ ) seems to have the ability to do this but the way the output is handled was different to a point where I was having difficulty working the two out to figure the correct was to do this. But without seeing the second sketch actually at work in a complete manner...

You have 3 items in your buffer for 4 digits, one is for the sign? For a negative sign all you want to do is turn on the G segment. You can do that by testing if the Fahrenheit reading is less than zero, then forcing the reading to be positive, and then follow with your conversion code to change the binary data to seven segment codes.

I believe G would be 0b00010000, you can stick that as the next item on your convention table.
Actually, I was under the impression that the G segment would be 0b01000000 but that may also depend on how the display is being driven. But for this, I'm sticking to common anode display

Extra bits added when I attempted to implement these.
// B01000000, //-
// B00000000 //all segments off
};

The void(loop) section are where I attempted these but without success.

You may also want to cut your problem into pieces, and fix each piece. Here that means don't read the thermometer, assign some dummy data and see it displays correctly. You would want to do this several times to check various numbers, something high positive, high negative, zero, and so on.
Perhaps I should dive deeper into doing that but wasn't understanding whether to try to tackle masking the leading zeroes or try to get the negative reading function first. (like chicken and egg to me for some reason). I first saw the issue as masking the zeroes but then masking the mask when reading (or the output) went negative for the sake of placing the '-' sign to the far left.

Leading zero suppression is a little trickier as it depends on the value you have. It can be the next step after you get the sign correct and you choose how to present this data: does the negative sign stay where it is or does it follow the data to the next spot over?
Yeah, I've literally exhausted all of google search on this area. Plenty of posts on how to add leading zeroes. But when it comes to finding information about how to remove them and I don't understand how they were put there in the first place... o_O
There are plenty of sketches that use a TM35 without this problem but translating that into DS18B20 use seemed a whole another rabbit hole for me.

Have you researched the format of the temperature value returned from the library?
I attempted to or I thought I had a basic understanding of the digits layout but it became lost on me when trying to see the differences in different sketches to make sense of some of it. The postings I've found didn't go into great detail to explain these and on the ones I looked into to try to learn their functions, the authors either don't respond to viewer's questions or may take an extraordinarily long time to respond if they respond at all.
 

djsfantasi

Joined Apr 11, 2010
5,571
Warning: this post is messed up and I am trying to correct it

Ok, using my crystal ball (and re-reading your post and code several times), I think I see a problem.

It had nothing to do with anything related to being able to deal with two's complements. It has to do with your code.

The code you present doesn't recognize that the temperature could be negative. Since I don't know that the returned range includes negative numbers, I would have coded for that.

Below is s block of code that I might use. It is very similar to your sketch, so you should be able to figure out how to use it.
Code:
boolean sign=false;

tmp = int(tempF*100);

sign = (tmp<0);
tmp = abs(tmp);

digitBuffer[2] = tmp/1000;
digitBuffer[1] = (tmp%1000)/100;
digitBuffer[0] = (tmp%100)/10;


Now, in your display routine, you have to figure out how to display the sign. You're only using three digits, so if "sign" is true, then you'd light the g segment of the last display.

I didn't read that part of your code closely, but if you defined another segment mask in your digit array, that only included the g segment, you could still use your display function code. Others have already suggested this.

Additionally, I don't see where you are displaying the temperature using the digitBuffer array. I would add another segment mask to your table, with all zeroes. Then, a simple while loop could examine digitBuffer and the leading zeroes (starting at the first digit, if it is a zero then it is a leading zero. If this is true, then if the next digit is zero.... and so on), could be replaced by the index to this table entry (tgatbus, all zeroes so no segments light).
 
Last edited:

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
It had nothing to do with anything related to being able to deal with two's complements. It has to do with your code.

The code you present doesn't recognize that the temperature could be negative. Since I don't know that the returned range includes negative numbers, I would have coded for that.
Right, the code posted did not have function for negative temp readings. When the reading went below zero degrees, the display became garbled.

I have added the code you have suggested and now negative temps are being read. But without the '-' sign for now.
***

C:
void loop()
{
  digitalWrite(ledPin, HIGH);
  boolean sign = false;
  sensors.requestTemperatures();
  tempC = sensors.getTempC(insideThermometer);
  tempF = DallasTemperature::toFahrenheit(tempC);
  tmp = int(tempF*100);

  if (tmp < 0){
  sign = true;
  tmp = abs(tmp);
  }
  }
***

I didn't read that part of your code closely, but if you defined another segment mask in your digit array, that only included the g segment, you could still use your display function code. Others have already suggested this.
The code posted was not written by me. I posted it in this manner to show it as original however, I have added the '-' and blank mask.

B01000000, //-
B00000000 //all segments off


Now, in your display routine, you have to figure out how to display the sign. You're only using three digits, so if "sign" is true, then you'd light the g segment of the last display.

I didn't read that part of your code closely, but if you defined another segment mask in your digit array, that only included the g segment, you could still use your display function code. Others have already suggested this.
Defining the bits for minus and blank mask was easy. It's trying to figure out how to implement them in the code is what I've been having so much trouble with.
 
Last edited:

ErnieM

Joined Apr 24, 2011
7,992
You should define what your display will show, as you almost have too much data for four digits. It just fits in Fahrenheit degrees.

Your range is -67.0 to +257.0 if you ignore the plus sign you can just fit this, negative reading will be "-XX.X" while positive readings will be "XXX.X" for four digits each.

For leading zeros... Which way will the data shift? For positive numbers you may want "_XX.X" or "__X.X" where "__0.X is allowed (not suppressing the zero before the decimal point). Or this can look like "0.X__" for small numbers, you must choose.

Same for negative numbers. "-XX.X" " and "_-X.X" would be my choice where the DP stays in place and the sign follows the most sig digit displayed.

I suggest you start with the display code and forget the temp sensor for the moment. Hit it with various data values, positive and negative, and get the display portion down cold before you send live data.

Please only post the code you are using. Posting some other code isn't helpful, it is actually confusing.

Please find the code tag button on the window where you post your messages and use that to insert your code. Always.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
You should define what your display will show, as you almost have too much data for four digits. It just fits in Fahrenheit degrees.
I thought that I had in the beginning, but now I see that it is not clear at all:
"I was able to move the decimal point over to read only tenths (000.0) instead of including hundredths (00.00) but I haven't been able to successful remove the leading zeros".

Your range is -67.0 to +257.0 if you ignore the plus sign you can just fit this, negative reading will be "-XX.X" while positive readings will be "XXX.X" for four digits each.
For this build, I had no intention of specifying a "C" or "F" in the display (and apparently, neither did the original author). Else, I would have to change the circuit to fit a fifth digit. The original build was set to display in Fahrenheit degrees. It can be easily changed to suit Celsius. I had no intention of adding a function to switch between C and F while in operation.

For leading zeros... Which way will the data shift? For positive numbers you may want "_XX.X" or "__X.X" where "__0.X is allowed (not suppressing the zero before the decimal point). Or this can look like "0.X__" for small numbers, you must choose.
So far the shift is set to indicate _XX.X and __X.X , That's pretty much how all the examples were that I've encountered. I had no consideration of the latter. Wouldn't having the extra zeros on the right side of the display be called trailing zeros?

The display is set up to drive four digit seven segment display using a single 74HC595. The display is numbered from left to right and is depicted in this way in the schematic. Using POV the digits are multiplexed rapidly to display four digits using one shift register.

Please only post the code you are using. Posting some other code isn't helpful, it is actually confusing.
Not sure that I follow you there. Currently, I've only been using the code I posted in the first post. The link leading to the second code was explained as a comparison in the first post that seemed to solve these issues.

Please find the code tag button on the window where you post your messages and use that to insert your code. Always.
I agree. This was missed in my second reply and has been corrected.

This is the full code that I currently have at this time with changes made:

C:
/*
* http://www.pial.net/arduino-controlling-a-4-digit-seven-segment-display/
*/

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

const int ledPin =  13;// LED connected to digital pin 13
const int latchPin = 8;//Pin connected to ST_CP of 74HC595
const int clockPin = 9;//Pin connected to SH_CP of 74HC595
const int dataPin = 10;//Pin connected to DS of 74HC595

const int digitPins[4] = {
  3,4,5,6}; //pins to control the 4 common anode pins of the display

const byte digit[12] = //seven segment digit bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01101101, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111, //9
  B01000000, //-
  B00000000  //all segments off
};

int digitBuffer[4] = {
  0};
int digitScan = 0;
int soft_scaler = 0;
float tempC, tempF;
int tmp;

void setup()  {  
  TCCR2A = 0;
  TCCR2B = (1<<CS21);
  TIMSK2 = (1<<TOIE2);
  TCNT2 = 0;
  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  for(int i=0;i<4;i++)
  {
  pinMode(digitPins[i],OUTPUT);
  }
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT); 

  sensors.begin();
  sensors.getAddress(insideThermometer, 0);
}

ISR(TIMER2_OVF_vect) {
  soft_scaler++;
  if(soft_scaler==15)
  {
  refreshDisplay();
  soft_scaler = 0;
  }
}; 

void refreshDisplay()
{
  for(byte k=0;k<4;k++)
  {
  digitalWrite(digitPins[k], LOW);
  }
  digitalWrite(latchPin, LOW); 
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(50);
  digitalWrite(digitPins[digitScan], HIGH);

  digitalWrite(latchPin, LOW); 
  if(digitScan==1)
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B10000000)); //inserting the dot
  }
  else
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~digit[digitBuffer[digitScan]]);
  }
  digitalWrite(latchPin, HIGH);
  digitScan++;
  if(digitScan>3) digitScan=0;
}

void loop()  
{
  digitalWrite(ledPin, HIGH);
  boolean sign = false;
  sensors.requestTemperatures();
  tempC = sensors.getTempC(insideThermometer);
  tempF = DallasTemperature::toFahrenheit(tempC);
  tmp = int(tempF*100);

  if (tmp < 0){
  sign = true;
  tmp = abs(tmp);
  }
  digitBuffer[2] = tmp/1000;
  digitBuffer[1] = (tmp%1000)/100;
  digitBuffer[0] = (tmp%100)/10;
  //digitBuffer[0] = (tmp%100)%10;
  Serial.print("Temp = ");
  Serial.println(tempF);
  digitalWrite(ledPin, LOW);
  delay(500);
}
Same for negative numbers. "-XX.X" " and "_-X.X" would be my choice where the DP stays in place and the sign follows the most sig digit displayed.
The decimal is not a floating decimal. It is set in one place. Not a clue how to do a floating minus sign. In fact, I'm not a programming expert, nor do I claim to be. That's why I came here.

I suggest you start with the display code and forget the temp sensor for the moment. Hit it with various data values, positive and negative, and get the display portion down cold before you send live data.
This is the sketch without sensor input. It gets a number assigned to a variable. That number gets broken down into its components but how exactly that works, I don't know entirely. My search experience on trying to find the answers has yielded limited results. That or I don't know what to search for.

The code for display only. a number get assigned to the variable 'tempF'.

C:
/*
* http://www.pial.net/arduino-controlling-a-4-digit-seven-segment-display/
*/

//#include <OneWire.h>
//#include <DallasTemperature.h>

//#define ONE_WIRE_BUS 2

//OneWire oneWire(ONE_WIRE_BUS);
//DallasTemperature sensors(&oneWire);
//DeviceAddress insideThermometer;

const int ledPin =  13;// LED connected to digital pin 13
const int latchPin = 8;//Pin connected to ST_CP of 74HC595
const int clockPin = 9;//Pin connected to SH_CP of 74HC595
const int dataPin = 10;//Pin connected to DS of 74HC595

const int digitPins[4] = {
  3,4,5,6}; //pins to control the 4 common anode pins of the display

const byte digit[10] = //seven segment digit bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01101101, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111, //9
//  B01000000, //-
//  B00000000  //all segments off
};

int digitBuffer[4] = {
  0};
int digitScan = 0;
int soft_scaler = 0;
float tempC, tempF;
int tmp;

void setup()  {  
  TCCR2A = 0;
  TCCR2B = (1<<CS21);
  TIMSK2 = (1<<TOIE2);
  TCNT2 = 0;
  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  for(int i=0;i<4;i++)
  {
  pinMode(digitPins[i],OUTPUT);
  }
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT); 

//  sensors.begin();
//  sensors.getAddress(insideThermometer, 0);
}

ISR(TIMER2_OVF_vect) {
  soft_scaler++;
  if(soft_scaler==15)
  {
  refreshDisplay();
  soft_scaler = 0;
  }
}; 

void refreshDisplay()
{
  for(byte k=0;k<4;k++)
  {
  digitalWrite(digitPins[k], LOW);
  }
  digitalWrite(latchPin, LOW); 
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(50);
  digitalWrite(digitPins[digitScan], HIGH);

  digitalWrite(latchPin, LOW); 
  if(digitScan==1)
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B10000000)); //inserting the dot
  }
  else
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~digit[digitBuffer[digitScan]]);
  }
  digitalWrite(latchPin, HIGH);
  digitScan++;
  if(digitScan>3) digitScan=0;
}

void loop()  
{
  digitalWrite(ledPin, HIGH);
  boolean sign = false;
  //sensors.requestTemperatures();
  //tempC = sensors.getTempC(insideThermometer);
  //tempF = DallasTemperature::toFahrenheit(tempC);
  //tmp = int(tempF*100);

  tempF = 9.0;  // This is the number being put to the display

  tmp = int(tempF*100);

  if (tmp < 0){
  sign = true;
  tmp = abs(tmp);
  }
  digitBuffer[2] = tmp/1000;
  digitBuffer[1] = (tmp%1000)/100;
  digitBuffer[0] = (tmp%100)/10;
  //digitBuffer[0] = (tmp%100)%10;
  Serial.print("number assigned to the variable 'tmp' = ");
  Serial.println(tmp);
  Serial.print("number assigned to the variable 'tmp' = ");
  Serial.println(tempF);
  digitalWrite(ledPin, LOW);
  delay(500);
}
When the number 9.0 is assigned to the variable 'tempF', I get 009.0 on the display.
serial output:
number assigned to the variable 'tmp' = 900
number assigned to the variable 'tempF' = 9.0

When the number 99.0 is assigned to the variable 'tempF', I get 099.0 on the display.
serial output:
number assigned to the variable 'tmp' = 9900
number assigned to the variable 'tempF' = 99.0

When the number 100.0 is assigned to the variable 'tempF', I get 0_0.0 on the display. - '_' being blank digit. Overflow?
serial output:
number assigned to the variable 'tmp' = 10000
number assigned to the variable 'tempF' = 100.0

When the number -99.0 is assigned to the variable 'tempF', I get 099.0 on the display. negative sign not programmed in yet.
serial output:
number assigned to the variable 'tmp' = 9900
number assigned to the variable 'tempF' = -99.0

I used to understand shift registers but that was 20 years ago and software wasn't involved. I'm obviously doing something wrong.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
I had but the library appears to interface the segments directly (buffered of course) vs. using a shift register. I was attempting to build with a shift register to free up pins for other purposes. While the library should work, it doesn't appear to leave any digital pins available for adding other functions afforded to a thermostat.

The short term goal of my objective was to build a temperature reader (that can read a wider range of temperatures including negative temps) and display the output on seven segment display. A basic platform for other projects that may come.

The long term goal was once the temperature reader was complete, then add functions to control a deep freezer. Use a digital pin to activate a relay to start the compressor when the temperature reaches a top set limit and deactivate the relay thus stopping the compressor when the low temperature limit is reached. Another digital pin would be used to control a piezo beeper for alarm if the temperature goes above the set top limit for too long. An analog pin would be used to read a reed switch for the door and set the alarm high if the door is open too long.
The long term goal is later.
 
Last edited:

djsfantasi

Joined Apr 11, 2010
5,571
In your last post, you suggest that using a shift register saves pins. It shouldn't. I am not clear on how it does so. Or at least, in the long run you are trading fewer pins for more memory and slower speed. At least take s look at the alternatives and see if there is some value there.

I see you running into one of the problems with stitching together code you've found online, without understanding what it does. I feel that I have given you a very specific solution to your problem. Yet you do not understand how it works. I guess I cannot find s way to tell you.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
In your last post, you suggest that using a shift register saves pins. It shouldn't. I am not clear on how it does so. Or at least, in the long run you are trading fewer pins for more memory and slower speed. At least take s look at the alternatives and see if there is some value there.
Actually, it does. If driving four digits directly (8 digital pins) and the four for the 4 anodes (4 digital pins) for a total of 12 digital pins (D2 -D13), that leaves you with no pins remaining to add other functions as mentioned previously such as adding a digital sensor.
Using a shift register, you are using three pins (RCLK, SRCLK, SER) and four pins (for the anodes) for a total of 7 digital pins with 4 pins remaining. The original sketch uses <25% of the total memory. So I don't think that 's an issue.

I see you running into one of the problems with stitching together code you've found online, without understanding what it does. I feel that I have given you a very specific solution to your problem. Yet you do not understand how it works. I guess I cannot find s way to tell you.
Of course I don't understand it completely. That's why I came here in hopes of finding some insight. Your solution worked - at dealing with the negative numbers being fed to the shift register. The garbled display is an indication of overflow. The abs() function helped this. You have suggested that I add masking bits for a blank and a minus. I was already aware of that. I was having trouble trying to implement the masking bits. As I explained earlier.
 

ErnieM

Joined Apr 24, 2011
7,992
When testing the display use data that has more meaning. Sending 0.1, 1.2, 12.3 and 123.4 will yield much more useful informant than using all nines.

Lines 118, 119, 120 do your number to segment data conversion. You only assign for three digits so it is no surprise you do not ever get four digits to display. So add another line there. And fix line 118 for the larger numbers it can see.

Actually all lines need to be corrected. These use the modulus opperator %. Do you know what this does?
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
When testing the display use data that has more meaning. Sending 0.1, 1.2, 12.3 and 123.4 will yield much more useful informant than using all nines.
Understood. Sending the numbers like that sounds more realistic

Lines 118, 119, 120 do your number to segment data conversion. You only assign for three digits so it is no surprise you do not ever get four digits to display. So add another line there. And fix line 118 for the larger numbers it can see.
These have been changed:
The display is displaying every number from -99.9 to 999.9 (minus the '-' sign) without trouble! Thank you!

C:
/*
* http://www.pial.net/arduino-controlling-a-4-digit-seven-segment-display/
*/

//#include <OneWire.h>
//#include <DallasTemperature.h>

//#define ONE_WIRE_BUS 2

//OneWire oneWire(ONE_WIRE_BUS);
//DallasTemperature sensors(&oneWire);
//DeviceAddress insideThermometer;

const int ledPin =  13;// LED connected to digital pin 13
const int latchPin = 8;//Pin connected to ST_CP of 74HC595
const int clockPin = 9;//Pin connected to SH_CP of 74HC595
const int dataPin = 10;//Pin connected to DS of 74HC595

const int digitPins[4] = {
  3,4,5,6}; //pins to control the 4 common anode pins of the display

const byte digit[10] = //seven segment digit bits
{
  B00111111, //0
  B00000110, //1
  B01011011, //2
  B01001111, //3
  B01100110, //4
  B01101101, //5
  B01111101, //6
  B00000111, //7
  B01111111, //8
  B01101111, //9
//  B01000000, //-
//  B00000000  //all segments off
};

int digitBuffer[4] = {
  0};
int digitScan = 0;
int soft_scaler = 0;
float tempC, tempF;
int tmp;

void setup()  {
  TCCR2A = 0;
  TCCR2B = (1<<CS21);
  TIMSK2 = (1<<TOIE2);
  TCNT2 = 0;
  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  for(int i=0;i<4;i++)
  {
  pinMode(digitPins[i],OUTPUT);
  }
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

//  sensors.begin();
//  sensors.getAddress(insideThermometer, 0);
}

ISR(TIMER2_OVF_vect) {
  soft_scaler++;
  if(soft_scaler==15)
  {
  refreshDisplay();
  soft_scaler = 0;
  }
};

void refreshDisplay()
{
  for(byte k=0;k<4;k++)
  {
  digitalWrite(digitPins[k], LOW);
  }
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(50);
  digitalWrite(digitPins[digitScan], HIGH);

  digitalWrite(latchPin, LOW);
  if(digitScan==1)
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B10000000)); //inserting the dot
  }
  else
  {
  shiftOut(dataPin, clockPin, MSBFIRST, ~digit[digitBuffer[digitScan]]);
  }
  digitalWrite(latchPin, HIGH);
  digitScan++;
  if(digitScan>3) digitScan=0;
}

void loop()
{
  digitalWrite(ledPin, HIGH);
  boolean sign = false;
  //sensors.requestTemperatures();
  //tempC = sensors.getTempC(insideThermometer);
  //tempF = DallasTemperature::toFahrenheit(tempC);
  //tmp = int(tempF*100);
  tempF = 123.4;
  tmp = int(tempF*10);

  if (tmp < 0){
  sign = true;
  tmp = abs(tmp);
  }

  digitBuffer[3] = int(tmp)/1000;
  digitBuffer[2] = (int(tmp)%1000)/100;
  digitBuffer[1] = (int(tmp)%100)/10;
  digitBuffer[0] = (int(tmp)%100)%10;
  Serial.print("number assigned to the variable 'tmp' = ");
  Serial.println(tmp);
  Serial.print("number assigned to the variable 'tempF' = ");
  Serial.println(tempF);
  digitalWrite(ledPin, LOW);
  delay(500);
}
Actually all lines need to be corrected. These use the modulus opperator %. Do you know what this does?
% is a division remainder. I see that more clearly now. I didn't understand it as I believe I didn't know what to look for.
Anyone have any suggestion on how to suppress the leading zeros? That's where I'm stuck.
 
Last edited:

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
I'm still unable to get rid of the leading zeros through several attempts. I tried adding a shift out routine similar to the decimal routine but it appears to get over written by an actual zero. But, it I attempt it past the digitBuffer set, I get strange characters but no blank. Has anyone had success at doing this or does anyone have an example of using the masking bit to overwrite the zero that they can share? I'm really stuck on this.
 

ErnieM

Joined Apr 24, 2011
7,992
Code.

If we no see your code we no have clue what you do.

Just the "void loop()" routine will suffice. I all should get done there.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
Code.

If we no see your code we no have clue what you do.

Just the "void loop()" routine will suffice. I all should get done there.
Sorry about that. It was very late for me. Some changes were attempted outside the void loop() but with no effects so they're excluded.

C:
void loop()
{
  digitalWrite(ledPin, HIGH);
  boolean sign = false;
  //sensors.requestTemperatures();
  //tempC = sensors.getTempC(insideThermometer);
  //tempF = DallasTemperature::toFahrenheit(tempC);
  //tmp = int(tempF*100);
  tempF = 10.3;
  tmp = int(tempF*10);

//  if (digitScan == 4 ){
//  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitScan] | B00000000)); //inserting the blank
//  }
//  else
//  {
//  shiftOut(dataPin, clockPin, MSBFIRST, digit[digitBuffer[digitScan]]);
//  }
  //if (tmp < 0){
  //sign = true;
  //tmp = abs(tmp);
  //}

// No effect here :/

//if (digitScan == 4) {
//  if (tmp < 0){
//  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B01000000)); // outputs minus sign
//  }
//  else {
//  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B00000000));
//  }

//  tmp = abs(tmp);
// }
//  else if (digitScan == 4 && digit[digitScan-1] == 0) { // writing on the second display (tens; if there are none, outputs nothing)
//  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B00000000));
//  }
//  else if (i == 2) { // writing on the third display, where decimal point is needed
//  shiftOut(data_pin, clk_pin, LSBFIRST, numbers_dot_lsb[digits[i-1]]);
//  }
//  else { // writing on "other" displays (in fact fourth display AND seecond display only if there are some tens)
//  shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[digits[i-1]]);
//  }


  digitBuffer[3] = int(tmp)/1000;
  digitBuffer[2] = (int(tmp)%1000)/100;
  digitBuffer[1] = (int(tmp)%100)/10;
  digitBuffer[0] = (int(tmp)%100)%10;

// The first thing to actually do something albeit wrong.
if (digitBuffer[3] == 0){
  shiftOut(dataPin, clockPin, MSBFIRST, ~(digit[digitBuffer[digitScan]] | B00000000));
  }
//  else{
//  if (digitBuffer[3] == 0){
//  digitBuffer[3] = B00000000; // definitely wrong- no example of how to correctly do this.
//  if ((digitBuffer[3] == 0) && (digitBuffer[2]) == 0){
//  digitBuffer[2] = 0;
//  }

//Another example tried but no effect. 
//  digitBuffer[3]=(digitBuffer[3]==0)?digit[11]:digitBuffer[3]; //eliminate leading zero for displayBuffer[3]
//  digitBuffer[2]=((digitBuffer[2]==0) && (digitBuffer[3]==digit[11]))?digit[11]:digitBuffer[1];


  Serial.print("number assigned to the variable 'tmp' = ");
  Serial.println(tmp);
  Serial.print("number assigned to the variable 'tempF' = ");
  Serial.println(tempF);
  digitalWrite(ledPin, LOW);
  delay(500);
}
 
Last edited:
Top