Introduction.
The following routines are presented to illustrate various applications of interfacing the OWC #190 with a PC.
All routines were written using the
In each of the routines, I attempt to provide simple examples of various application, using and RS232 Com Port, timing, interfacing with a DS18B20 temperature sensors, a DS2438 temperature and A/D, a DS2423 dual counter, a DS2450 Quad 16-bit A/D, measuring temperature using a NTC thermistor, reading parameters from a text file, logging data to a file, timing a process, etc.
I was educated as an EE and although I do indeed teach an introductory C course, I really perceive myself as a novice when it comes to process control using a PC. Please recognize I wrote and tested the routines. However, I would be pompous to offer that I have not made an error or two. In addition, good programming demands that the programmer anticipate and deal with every possible error condition. I can't do that as there are not enough hours in the day and a 300 line program might grow to several times that size. But, hopefully, this offers ideas and helps some realize their creativity.
Serial Port Routines.
This discussion has not been completed.
p>
In this program, three DS18B20 temperature sensors are connected to OWC #190 channels "0", "1" and "2". LEDs are connected to OWC #190 channels "3", "4" and "5". The LEDs might be alarms, heaters and cooling fans or similar. In this example, allarm outputs 3, 4 and 5 are associated with temperature sensors 0, 1 and 2, respectively.
The high and low trip points and the polarity are specified in a file c:\settings.txt. This is a file you prepare to define the operation of your system as opposed to hard coding the trip points in your program.
Consder the file;
23.0 27.0 1 3.0 7.0 0 30.0 35.0 1The first line might be used to control an air conditioner in a living area. When the temperature is above 27.0, turn on the LED (air conditioner) and leave it on until the temperature falls below 23.0 degrees C. Note that I define this a polarity of 1.
The second might be near an exposed water pipe in a cool climate. If ther temperature falls below 3.0 degrees C, turn on a heater until the temperature rises above the high trip point of 7.0 degrees C. Note that the polarity is identified as 0.
The third might be used to control a ventilation fan in an attic. When the temperatures rises above 35.0, turn on the fan, and leave it on until the temperature falls below 30 degrees C.
Note that I have included the implementation of the RS232 functions in
// DS18B20_2.CPP (Bloodshed DevCpp) // // Illustrates an interface with three DS18B20 temperature sensors on // OWC #190 channel 0, 1, 2. Each temperature is measured and compared // with high and low trip points. Corresponding relays (or LEDs) on // OWC #190 channels 3, 4 and 5 are either operated or released. // // The measurment sequence is repeated every minute. // // copyright, Peter H Anderson, Sept 20, '07 // #include <conio.h> #include <stdio.h> #include <windows.h> #include <time.h> #include <string.h> #define COM_PORT 3 // <<<<<<<<<<<< Modify as required. #define DEBUG int meas_DS18B20(int ch, float *p_Tc); int output_state(int ch, int state); // RS232 Routines HANDLE rs_initialise (const long int BaudRate, const char parity, const char data); void rs_flush(void); void rs_terminate(void); char rs_getch(void); int rs_getline(char line[], clock_t timeout); void rs_putch(int txchar); void rs_putstr(const char *string); // delay_routines void delay_ms(clock_t millis); HANDLE hCom; //handle for serial port I/O int io_port; #define FAILURE 0 #define SUCCESS !FAILURE int main() { FILE *fr; clock_t timeout; float Tc[3], Tc_high_trip[3], Tc_low_trip[3]; int n, channel, pol[3]; io_port = COM_PORT; if(!rs_initialise(9600, '8', 'N')) // open the COM port { printf("Opening Port Failed\n"); delay_ms(5000); exit(1); } if ((fr = fopen("c:\\settings.txt", "rt")) == NULL) { printf("Error opening thermostat settings file\n"); delay_ms(5000); exit(1); } // else for (n=0; n<3; n++) { if (fscanf(fr, "%f %f %d", &Tc_high_trip[n], &Tc_low_trip[n], &pol[n]) != 3) { printf("Error reading from thermostat settings file\n"); exit(1); } else { printf("%f\t%f\t%d\n", Tc_high_trip[n], Tc_low_trip[n], pol[n]); } } // now proceed to measure timeout = clock() + 60000; // every 60 secs while(1) // forever { for (channel = 0; channel<3; channel++) { if (meas_DS18B20(channel, &Tc[channel]) == FAILURE) // perform a temperature meas // on CH n { printf("CH %1d, Error\n", channel); } else { printf("CH %1d, Tc = %.2f\n", channel, Tc[channel]); } if (pol[channel] == 1) { if (Tc[channel] > Tc_high_trip[channel]) { output_state(channel + 3, 1); } if (Tc[channel] < Tc_low_trip[channel]) { output_state(channel + 3, 0); } } else // pol = 0 { if (Tc[channel] > Tc_high_trip[channel]) { output_state(channel + 3, 0); } if (Tc[channel] < Tc_low_trip[channel]) { output_state(channel + 3, 1); } } } // end of for loop while(clock() < timeout) // wait for the balance of the 60 seconds { } timeout = timeout + 60000; // new timeout value } // as written, the program never really gets to this point rs_terminate(); while(getchar() != 'x') { } return 0; } int meas_DS18B20(int ch, float *p_Tc) { char s[25], line[25]; int num_chars, high, low, word; float Tc; sprintf(s, "P%1dW%1dccS%1d44", ch, ch, ch); // presence, 0xcc, 0x44 followed by one second of strong pullup rs_putstr(s); delay_ms(1200); sprintf(s, "P%1dW%1dccW%1dbe", ch, ch, ch); // presence, 0xcc, 0xbe rs_putstr(s); delay_ms(200); rs_flush(); // flush the buffer sprintf(s, "R%1d", ch); rs_putstr(s); delay_ms(50); if ((num_chars = rs_getline(line, 100)) == 0) // probably no device { return(FAILURE); } // printf("%s\n", line); // used for debugging // else sscanf(line, "%x", &low); rs_flush(); // flush buffer sprintf(s, "R%1d", ch); rs_putstr(s); delay_ms(50); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } // printf("%s\n", line); // else sscanf(line, "%x", &high); word = (high << 8) | low; // printf("....%x %x %x\n", high, low, word); // for debugging if (word & 0x8000) { word = (word ^ 0xffff) + 1; Tc = -0.0625 * (float) word; } else { Tc = 0.0625 * (float) word; } *p_Tc = Tc; return(SUCCESS); } int output_state(int ch, int state) { char s[25], line[25]; int num_chars, relay_state; rs_flush(); if (state == 1) { sprintf(s, "H%1d", ch); // H followed by the channel } else { sprintf(s, "L%1d", ch); // L followed by the channel } rs_putstr(s); num_chars = rs_getline(line, 250); sscanf(line, "%d", &relay_state); return(relay_state); } void delay_ms(clock_t millis) { clock_t endtime; endtime = millis + clock(); while( endtime > clock() ) ; } // RS232 routines HANDLE rs_initialise (const long int BaudRate, const char parity, const char data) { BOOL bPortReady; DCB dcb; COMMTIMEOUTS CommTimeouts; char ComPortName[5]="COM "; ComPortName[3]='0'+ io_port; hCom = CreateFile(ComPortName, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // no security OPEN_EXISTING, 0, // no overlapped I/O NULL); // null template if ((int)hCom <= 0) { printf("serial port COM%d connect fail %s error %d\n\r", io_port, ComPortName, GetLastError()); return 0; } //else printf(" serial port COM%d connect OK \n\r", io_port); bPortReady = SetupComm(hCom, 128, 128); // set buffer sizes if (!bPortReady ) { printf("serial port COM%d SetupComm fail %d\n\r", io_port, GetLastError()); return 0; } //else printf(" serial port COM%d connect OK \n\r", io_port); bPortReady = GetCommState(hCom, &dcb); if (!bPortReady ) { printf("serial port COM%d GetCommState fail %d\n\r", io_port, GetLastError()); return 0; } // else printf(" serial port COM%d connect OK \n\r", io_port); dcb.BaudRate = BaudRate; if( data == '7') dcb.ByteSize = 7; else dcb.ByteSize = 8; if( parity == 'E') dcb.Parity = EVENPARITY; if( parity == 'O') dcb.Parity = ODDPARITY; else dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.fAbortOnError = TRUE; // set XON/XOFF dcb.fOutX = FALSE; // XON/XOFF off for transmit dcb.fInX = FALSE; // XON/XOFF off for receive // set RTSCTS dcb.fOutxCtsFlow = FALSE; // turn off CTS flow control dcb.fRtsControl = FALSE; // RTS_CONTROL_HANDSHAKE; // // set DSRDTR dcb.fOutxDsrFlow = FALSE; // turn off DSR flow control //dcb.fDtrControl = DTR_CONTROL_ENABLE; // DTR handshake dcb.fDtrControl = DTR_CONTROL_DISABLE; // // dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; // bPortReady = SetCommState(hCom, &dcb); if (!bPortReady ) { printf("serial port COM%d SetCommState fail %d\n\r", io_port, GetLastError()); return 0; } // Communication timeouts //COMMTIMEOUTS CommTimeouts; bPortReady = GetCommTimeouts (hCom, &CommTimeouts); CommTimeouts.ReadIntervalTimeout = 5 ; CommTimeouts.ReadTotalTimeoutConstant = 5 ; CommTimeouts.ReadTotalTimeoutMultiplier = 1 ; CommTimeouts.WriteTotalTimeoutConstant = 5 ; CommTimeouts.WriteTotalTimeoutMultiplier = 1 ; bPortReady = SetCommTimeouts (hCom, &CommTimeouts); if (!bPortReady ) { printf("serial port COM%d SetCommTimeouts fail %d\n\r", io_port, GetLastError()); return 0; } else { printf(" serial port COM%d connect OK \n\r", io_port); } return (hCom); } void rs_terminate(void) { CloseHandle(hCom); } char rs_getch(void) { char rxchar; BOOL bReadRC; static DWORD iBytesRead; bReadRC = ReadFile(hCom, &rxchar, 1, &iBytesRead, NULL); if (iBytesRead) { return rxchar; } else { return 0; // return 0 if no character read } } void rs_flush(void) { while(rs_getch()!=0) ; } int rs_getline(char line[], clock_t timeout) { int num_chars = 0; char ch; clock_t endtime; endtime = timeout + clock(); //printf("%ld %ld\n", clock(), endtime); while(endtime > clock()) { //printf("!"); ch = rs_getch(); //printf("%d ", ch); if (ch != 0) { //printf("%c", ch); if ((ch == 10) || (ch == 13)) { line[num_chars] = '\0'; // terminate the string return(num_chars); } else { line[num_chars] = ch; ++num_chars; } } } // end of while line[num_chars] = '\0'; return(-1); // timeout } void rs_putch(int txchar) { BOOL bWriteRC; static DWORD iBytesWritten; bWriteRC = WriteFile(hCom, &txchar, 1, &iBytesWritten,NULL); return; } void rs_putstr(const char *string) { #ifdef DEBUG printf("%s\n", string); #endif while (*string != '\0') { delay_ms(5); rs_putch(*string++); } }
This program illustrates an interface with the DS2438 Smart Battery monitor which includes the capability for measuring temperature, for measuring one external voltage and the voltage of the supply to the DS2438. (The DS2438 also has another A/D to measure the current though a sensing resistor and measuring elapsed time. I have not used these capabilities.
This routine measures and displays the temperature, the voltage on the A/D and the supply voltage. This is repeated every minute.
A Honeywell HIH-4000 is connected to the A/D of the DS2438 and the relative humidity is calculated and adjusted for the measured temperature.
Note that I have an RH #166 unit which uses a DS2438 and a Honeywell HIH-4000 to measure temperature and relative humidity and in fact used one to test this routine.
The primary functions;
int meas_DS2438(int ch, float *p_Tc, float *p_Vad, float *p_Vd);Note that the temperature, Vad and Vdd are passed by reference and the return integer is used for error handling. As noted above, I did very little with error handling.
int meas_DS2438_temp(int ch, float *p_Tc); int meas_DS2438_voltage(int ch, int source, float *p_V);The function meas_DS2438_voltage is used to measure either the Vad or the Vdd by passing soource as either a zero or a one.
float calc_RH(float Tc, float Vad, float Vdd);This function uses Vad, Vdd and the temperature to calculate the relative humidity corrected for temperature.
From the Honeywell data sheet,
Vout = Vsource (0.00062 * RH_raw + 0.16)Rerranging to express RH as a function of Vout and Vsource;
RH_raw = 161.3 * Vad / Vdd - 25.8;This value is corrected for temperature;
RH_corrected = RH_raw / (1.0305 + 0.000044 * Tc - 0.0000011 * Tc^2)
Note that in the following, the implementations the the RS232 functions has been ommitted fro brevity. You can simply cut and paste these from the DS18B20 routine which appears above.
// DS2438_1.CPP (Bloodshed DevCpp) for the OWC #190 // // Illustrates an interface with a DS2438 temperature and A/D IC. A Honeywell // HIH-4000 is connected to the A/D input. // // Measures and displays temperature, Vad and Vdd. // // Calculates and displays relative humidity. // // copyright, Peter H Anderson, Sept 26, '07 // #include <conio.h> #include <stdio.h> #include <windows.h> #include <time.h> #include <string.h> #define COM_PORT 3 // <<<<<<<<<<<< Modify as required. #define DEBUG int meas_DS2438(int ch, float *p_Tc, float *p_Vad, float *p_Vd); int meas_DS2438_temp(int ch, float *p_Tc); int meas_DS2438_voltage(int ch, int source, float *p_V); float calc_RH(float Tc, float Vad, float Vdd); int output_state(int ch, int state); // RS232 Routines HANDLE rs_initialise (const long int BaudRate, const char parity, const char data); void rs_flush(void); void rs_terminate(void); char rs_getch(void); int rs_getline(char line[], clock_t timeout); void rs_putch(int txchar); void rs_putstr(const char *string); // delay_routines void delay_ms(clock_t millis); HANDLE hCom; //handle for serial port I/O int io_port; #define FAILURE 0 #define SUCCESS !FAILURE int main() { io_port = COM_PORT; clock_t timeout; float Tc, Vad, Vdd, RH; int channel, n; if(!rs_initialise(9600, '8', 'N')) // open the COM port { printf("Opening Port Failed"); delay_ms(5000); exit(1); } timeout = clock() + 60000; // every 60 secs channel = 1; while(1) // forever { if (meas_DS2438(channel, &Tc, &Vad, &Vdd) == FAILURE) // perform a temperature meas and two A/D on specified // channel { printf("CH %1d, Error\n", channel); } else { RH = calc_RH(Tc, Vad, Vdd); printf("CH %1d, Tc = %.2f, Vad = %.2f, Vdd = %.2f, RH = %.2f\n", channel, Tc, Vad, Vdd, RH); } while(clock() < timeout) // wait for the balance of the 60 seconds { } timeout = timeout + 60000; // new timeout value } // as written, the program never really gets to this point rs_terminate(); while(getchar() != 'x') { } return 0; } int meas_DS2438(int ch, float *p_Tc, float *p_Vad, float *p_Vd) { if (meas_DS2438_temp(ch, p_Tc) == FAILURE) { return(FAILURE); } if (meas_DS2438_voltage(ch, 0, p_Vad) == FAILURE) { return(FAILURE); } if (meas_DS2438_voltage(ch, 1, p_Vd) == FAILURE) { return(FAILURE); } } int meas_DS2438_temp(int ch, float *p_Tc) { char s[25], line[25]; int num_chars, high, low, word_val, sign_bit; float Tc; sprintf(s, "P%1dW%1dccW%1d44", ch, ch, ch, ch); // presence, CC, 44 - perform temperature measurement rs_putstr(s); delay_ms(1100); // allow time for temperature meas to complete sprintf(s, "P%1dW%1dccW%1db8W%1d00", ch, ch, ch, ch); // presence, CC, B8, 00 - recall to scratchpad rs_putstr(s); sprintf(s, "P%1dW%1dccW%1dbeW%1d00", ch, ch, ch, ch); // presence, CC, BE, 00 - read scratchpad rs_putstr(s); rs_flush(); // skip byte 0 sprintf(s, "R%1d", ch); rs_putstr(s); delay_ms(100); rs_flush(); sprintf(s, "R%1d", ch); rs_putstr(s); delay_ms(100); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } // else sscanf(line, "%x", &low); rs_flush(); // flush buffer sprintf(s, "R%1d", ch); rs_putstr(s); delay_ms(100); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } // else sscanf(line, "%x", &high); printf("%x %x\n", high, low); // used for debugging word_val = (unsigned int) high * 256 + low; sign_bit = word_val & 0x8000; if (sign_bit) { word_val = word_val ^ 0xffff + 1; // take the twos comp } word_val = word_val / 8; Tc = 0.03125 * (float) word_val; if (sign_bit) { Tc = -Tc; } *p_Tc = Tc; return(SUCCESS); } int meas_DS2438_voltage(int ch, int source, float *p_V) { char s[25], line[25]; int num_chars, word_val, n, a[9]; float Tc; if (source == 0) // V_ad { sprintf(s, "P%1dW%1dccW%1d4eW%1d00W%1d00", ch, ch, ch, ch, ch); // presence, CC, 4E, 00, 00 for Vad rs_putstr(s); } else // Vdd { sprintf(s, "P%1dW%1dccW%1d4eW%1d00W%1d08", ch, ch, ch, ch, ch); // presence, CC, 4E, 00, 08 for Vdd rs_putstr(s); } sprintf(s, "P%1dW%1dccW%1db4", ch, ch, ch); // presence, CC, 44 - perform A/D measurement rs_putstr(s); delay_ms(1200); sprintf(s, "P%1dW%1dccW%1db8W%1d00", ch, ch, ch, ch); // presence, CC, B8, 00 - recall to scratchpad rs_putstr(s); sprintf(s, "P%1dW%1dccW%1dbeW%1d00", ch, ch, ch, ch); // presence, CC, BE, 00 - read scratchpad rs_putstr(s); for (n=0; n<9; n++) { rs_flush(); // flush buffer sprintf(s, "R%1d", ch); rs_putstr(s); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } // else sscanf(line, "%x", &a[n]); // printf("....%d\n", high); } word_val = a[4] * 256 + a[3]; *p_V = 0.01 * (float) word_val; return(SUCCESS); } float calc_RH(float Tc, float Vad, float Vdd) { float RH_raw, RH_corrected; RH_raw = 161.3 * Vad / Vdd - 25.8; RH_corrected = RH_raw / (1.0305 + 0.000044 * Tc - 0.0000011 * Tc * Tc); printf("RH_raw = %.2f RH_corrected = %.2f\n", RH_raw, RH_corrected); return(RH_corrected); } void delay_ms(clock_t millis) { clock_t endtime; endtime = millis + clock(); while( endtime > clock() ) ; } // Include the implementations of the RS232 functions here.
DS2423.CPP
// description
// DS2423_1.CPP (Bloodshed Dev-Cpp) OWC #190 // // Illustrates an interface with a DS2423 dual 32-bit counter. // // copyright, Peter H Anderson, Sept 29, '07 // #include <conio.h> #include <stdio.h> #include <windows.h> #include <time.h> #include <string.h> #define COM_PORT 3 // <<<<<<<<<<<< Modify as required. #define DEBUG int meas_DS2423(int ch, long *p_count_0, long *p_count_1); // RS232 Routines HANDLE rs_initialise (const long int BaudRate, const char parity, const char data); void rs_flush(void); void rs_terminate(void); char rs_getch(void); int rs_getline(char line[], clock_t timeout); void rs_putch(int txchar); void rs_putstr(const char *string); // delay_routines void delay_ms(clock_t millis); HANDLE hCom; //handle for serial port I/O int io_port; #define FAILURE 0 #define SUCCESS !FAILURE int main() { clock_t timeout; long count_0, count_1; io_port = COM_PORT; if(!rs_initialise(9600, '8', 'N')) // open the COM port { printf("Opening Port Failed"); delay_ms(5000); exit(1); } timeout = clock() + 60000; // every 60 secs while(1) // forever { if (meas_DS2423(0, &count_0, &count_1) == FAILURE) { printf("CH 2, Error\n"); } else { printf("CH 2, Count_0 = = %lx, Count_1 = %lx\n", count_0, count_1); } while(clock() < timeout) // wait for the balance of the 60 seconds { } timeout = timeout + 60000; // new timeout value } // as written, the program never really gets to this point rs_terminate(); while(getchar() != 'x') { } return 0; } int meas_DS2423(int ch, long *p_count_0, long *p_count_1) { char s[25], line[25]; int num_chars, counter_num, n, a[4] = {0, 0, 0, 0}; unsigned long count; rs_putstr(s); for (counter_num=0; counter_num<=1; counter_num++) { if (counter_num == 0) { sprintf(s, "P%1dW%1dccW%1da5W%1dc0W%1d01", ch, ch, ch, ch, ch); // presence, CC, A5, C0, 01 rs_putstr(s); } else { sprintf(s, "P%1dW%1dccW%1da5W%1de0W%1d01", ch, ch, ch, ch, ch); rs_putstr(s); // presence, CC, A5, E0, 01 } // now 32 reads and throw away the data for (n=0; n<32; n++) { rs_flush(); // flush buffer sprintf(s, "R%1d", ch); rs_putstr(s); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } delay_ms(5); } // read the four data bytes for (n=0; n<4; n++) { rs_flush(); // flush buffer sprintf(s, "R%1d", ch); rs_putstr(s); if ((num_chars = rs_getline(line, 100)) == 0) { return(FAILURE); } // else sscanf(line, "%x", &a[n]); } count = a[3]; count = count * 256 + a[2]; count = count * 256 + a[1]; count = count * 256 + a[0]; printf("....%lx\n", count); if (counter_num == 0) { *p_count_0 = count; } else { *p_count_1 = count; } } return(SUCCESS); } void delay_ms(clock_t millis) { clock_t endtime; endtime = millis + clock(); while( endtime > clock() ) ; } // insert implementations of RS232 routines here