Monday, August 31, 2020

Serial Port Thermometer using DS1621


Hi there. In this article, I will discuss a project that I have postponed to write about for a long time. I am somehow stuck, on the article series about hard disks. I was postponing writing constantly to investigate further. At one point, I saw that I could not continue to write immediately and therefore decided to write a different topic.

I first saw this circuit, that I will discuss here, in Güçlü TUĞAY's Electronic Hobby book. In this book, he wrote that the circuit does not belong to him either and gave the resources he used. I have the sixth edition of the book dated January 2006 and unfortunately the given links are dead now. Although it is a turkish book, if you own it, it is fine because a circuit diagram (revised from the original, I suppose) exists in this book and Visual Basic (VB) codes can also be downloaded from the link above. There is still another problem for me and that is, I mainly use linux and even if I would have used Windows, it is difficult to find a VB compiler to compile the code because VB is now obsolete. About two years ago, VB 2005 Express Edition was available for download on Microsoft's website and I remember it was really hard to find.

The Internet Wayback machine has helped me greatly about the broken links. Circuit diagram, parts list, how circuit works and VB codes are still available in the archived webpage dated 09.02.2005. In this page, there is even its PCB layout, but I will set up the circuit on protoboard. Moreover, as can be seen, the man's name is "Alberto Ricci Bitti" who owns this page and when I search his name on google, I found another copy of the page at http://web.tiscali.it/riccibitti. Luckily, I found linux version of the same software written in C, in "Useful Links" section at the bottom of the page. Otherwise, porting the code from VB to C would cause a lot of trouble for me. And of course, this page is also in the internet archives: Linux thermometer project (rev v0.3, 26.11.2003).


How the Circuit Works
The thermometer block of the circuit consists of a single integrated circuit. I had previously written about DS1621 in I2C with Arduino article. Therefore, I will not go into much detail here again. The circuit diagram is given below:
 
PC Thermometer circuit diagram

I will not use IC U2 in this project because I don't need a second sensor. In the previous Arduino article, I wrote that using its 3 addressing pins, AT2432 (EEPROM IC) can be used up to 8 ICs in same I2C bus together. Same feature also exist on DS1621s (I forgot to mention in the previous article). Therefore, temperature can be measured with from up to 8 different points using different sensors. In the circuit diagram above, all addressing pins of U1 (A0, A1 and A2) are connected to ground but A0 addressing pin of U2 is connected to Vdd.

The upper half of the circuit is +5V voltage regulator. According to RS232 standard, serial port voltage range for logical 0 lays between +3V and +15V and logical 1 lays between -3V and -15V. This voltage from DTR and RTS pins comes to C1 and Vin pin of the regulator through D1 and D2, which acts as a half wave rectifier and it supplies +5V to Vdd pin of the IC through Vout and C2. Capacitors store energy, allowing temperature read cycles to be completed.

Similarly, the voltage from these pins (and from CTS pin) is reduced to +5V, which is the operating voltage of I2C, with the component pairs R2, DZ1 and R1, DZ2. +5V, which IC will be sending through SDA pin, lays within the operating voltage of serial port and can be read successfully. The serial data coming from SDA, is read by the software through serial port.

I set up the circuit on a protoboard. The circuit is not complicated, but it can be confusing since too many times, there are more then one connection from one single point (especially connected to SCL bus). Each time I set up this circuit, I succeeded at least in my second try.


Parts List:
2x 1N4148 Diodes
2x 5.1V 1/4W Zeners
2x 4.7K Resistors
1x 100nF Capacitor*
2x 47μF 16V Capacitor
1x IC DS1621
1x LM2940CT-5 Voltage Regulator (5V)

* 100nF capacitor(s) considered as bypass capacitors. Therefore, they must be placed very closely to the supply pin of each IC. On the other hand, I did not placed them in the circuit. It still works well "enough".


Reading the Temperature Values
As I mentioned previously, the circuit is connected to the computer over the serial port. It is hard to find a computer with serial port these days. Impossible on laptops and even rarely on desktop machines. Exceptionally, I have an old laptop with serial port. However, if you have an USB to serial converter, this will also work (unlike USB to parallel port converters). I had a male - female serial cable and used it as an extension cord, as can be seen on photos.

There are two years between the photo below and the next one. They look therefore quite different. In the photo on the right, I added an LED (with 870 ohm resistor) to the circuit, to be able to see how long +5V is supplied to the circuit from capacitors when a read request is sent from serial port. The voltage is supplied to the circuit for a considerably short time. In other words, the circuit is mostly off.

In the photo below, on the other hand, I placed the IC a little far from voltage regulator, to get simulate temperature changes by pressing my finger.


I downloaded the software from the link, I gave above. PollOnce() function in main.c file calls ReadTemp() function in readtemp.c file with sensor number as an argument. Before that, the COM port, to which the device is connected, is opened using Open_ComPort()function in i2cbus.c.

The ReadTemp()function is quite simple. The I2C_Start() and I2C_Stop() functions are placing start and stop bit sequences to the I2C bus as their names suggest. Arguments of I2C_Tx_byte() function are the address of DS1621 and its commands. The list of commands can be found in DS1621 datasheet. 0x90 is the base address of DS1621. When sensor address is added to this constant, I2C address of the specific chip is obtained. 0xEE command sends a temperature read request. Using 0xAA command, the temperature value is placed into the bus and these value is read from the bus from the bus using I2C_Rx_byte() function. The first byte is the integer part of the temperature and the second byte is the decimal part. Since DS1621's thermal resolution is half degree, the second byte can be either 0 or 128. At the temperatures below zero, DS1621 sets the first bit of the first byte (sign bit), so that 255 is read from the bus for -1 degree. But since our machine word is larger than 8 bit, the bytes will be read as a number greater than 128 for below zero temperatures, instead of eighth bit is considered as sign bit. For this reason, it is needed to subtract the integer part from 256 for the values greater than 128, to convert them to real temperature.

Functions in i2cbus.c file allows the bytes to be sent / received to be adapted to I2C protocol in order to allow serial port communication of the program. I2C_{T|R}x_byte() functions take a byte argument, process it bit by bit and place them as signals of zeros and ones for SDA and SCL pins in correct order.

However there is a serious problem with this code. The code does not work properly on today's fast computers. Therefore, minor changes had to be made. 

In main.c;
a) I assigned an initial value to i2wait variable by inserting "int i2wait = 0" to line 11. This will be used as a wait parameter.
b) At line 88, I inserted following lines after case 'l': block:
case 'w':
  i2wait = atoi( argv[i + 1] );
break;

Thus, wait time can be given from command line by using -w parameter. 

In i2cbus.c;
c) By adding "extern int i2wait;" to line 24, I defined the global i2wait variable here, which is originally defined in main.c.
d) I called Wait() function at the end of SCL_High(), SCL_Low(), SDA_High(), SDA_Low() functions. Lines 171, 176, 181 and 186 in the original file.
e) Finally, I inserted usleep(i2wait); to the line 203.

Latest version of the package, which contains these changes above, can be downloaded from this link. Just one make command is enough to compile. Finally, I added my user to the "dialout" group to be able to run the program under my user in Linux Mint. If the program does not run with your own user, but with sudo, the problem is with the permission to access serial port.

gettemp is not too difficult to use. First, I check with dmesg, which serial ports are available:

dmesg | grep -i "serial\|tty"
[    1.188451] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
[    1.188486] serial 00:04: [io  0x03f8-0x03ff]
[    1.188570] serial 00:04: [irq 4]
[    1.189083] serial 00:04: activated
[    1.209764] 00:04: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

Since I have a physical serial port, it is detected as soon as the kernel is loaded. If a USB to serial adapter is used, the command watch "dmesg | tail" can be used to find the serial port which is detected shortly. Since my serial port is ttyS0 according to the output above, I have to give "0" to -c parameter. I also set I2C address of the IC to 0, therefore I have to use -p 0 parameter, too. Debug mode can be optionally activated with -d 1 (or 2) parameter. Careful readers will notice that, although I introduced 'w' parameter, I am not using it. Because even an empty Wait() function provides enough delay for read cycle to complete.

Practical use of this circuit might not be on a computer, but rather with devices that have serial ports. I recorded daily temperature of my room minute by minute by putting gettemp in crontab and redirecting its output to a file. The ability to connect up to eight sensors from a single serial port might seem important at the first glance, however since the maximal length of I2C bus is limited to 1-2 meters, its only usage might be in places where high temperature changes are observed in a narrow space. (OK, long distance data communication is not my speciality, but according to the answer, if you are going to use it for long distances, choosing other protocols instead of I2C is suggested).

20 degrees today