#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdint.h>
int openPort(char port[]);
int presence(int sfd);
void writeBit(int sfd, int b);
uint8_t readBit(int sfd);
void writeByte(int sfd, int byte);
int readByte(int sfd);
int readByte(int sfd);
float getTemperature(int sfd);
int convert(int sfd);
uint8_t crc8(uint8_t *data, uint8_t len);
int main(int argc, char** argv) {
int sfd = openPort("/dev/ttyAMA1");
if (presence(sfd) == 0) {
printf("Device Present\n\r");
} else {
printf("No Device\n\r");
}
for(;;){
float temp=getTemperature(sfd);
printf("%f \n\r", temp);
fflush(stdout);
}
close(sfd);
return 0;
}
int openPort(char port[]) {
int sfd = open(port, O_RDWR | O_NOCTTY);
if (sfd == -1) {
printf("Error no is : %d\n", errno);
printf("Error description is : %s\n", strerror(errno));
return (-1);
};
struct termios options;
tcgetattr(sfd, &options);
cfsetspeed(&options, B115200);
cfmakeraw(&options);
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= PARENB;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag |= CLOCAL;
options.c_cflag |= CREAD;
options.c_cc[VTIME] = 0;
options.c_cc[VMIN] = 1;
tcsetattr(sfd, TCSADRAIN, &options);
return sfd;
}
int presence(int sfd) {
struct termios options;
tcgetattr(sfd, &options);
cfsetspeed(&options, B9600);
tcsetattr(sfd, TCSADRAIN, &options);
char buf = 0xF0;
int count = write(sfd, &buf, 1);
count = read(sfd, &buf, 1);
tcgetattr(sfd, &options);
cfsetspeed(&options, B115200);
tcsetattr(sfd, TCSADRAIN, &options);
if (buf == 0xF0) return -1;
return 0;
}
void writeBit(int sfd, int b) {
char buf;
if (b == 0) {
buf = 0x00;
} else {
buf = 0xFF;
}
int count = write(sfd, &buf, 1);
count = read(sfd, &buf, 1);
}
uint8_t readBit(int sfd) {
char buf;
buf = 0xFF;
int count = write(sfd, &buf, 1);
count = read(sfd, &buf, 1);
if (buf == 0xFF) return 1;
return 0;
}
void writeByte(int sfd, int byte) {
for (int i = 0; i < 8; i++) {
if (byte & 1) {
writeBit(sfd, 1);
} else {
writeBit(sfd, 0);
}
byte = byte >> 1;
}
}
int readByte(int sfd) {
int byte = 0;
for (int i = 0; i < 8; i++) {
byte = byte | readBit(sfd) << i;
};
return byte;
}
int convert(int sfd) {
int i;
writeByte(sfd, 0x44);
for (i = 0; i < 5000; i++) {
usleep(100000);
if (readBit(sfd) != 0)break;
}
return i;
}
uint8_t crc8(uint8_t *data, uint8_t len) {
uint8_t i;
uint8_t j;
uint8_t temp;
uint8_t databyte;
uint8_t crc = 0;
for (i = 0; i < len; i++) {
databyte = data[i];
for (j = 0; j < 8; j++) {
temp = (crc ^ databyte) & 0x01;
crc >>= 1;
if (temp)
crc ^= 0x8C;
databyte >>= 1;
}
}
return crc;
}
float getTemperature(int sfd) {
if (presence(sfd) == -1) return -1000;
writeByte(sfd, 0xCC);
if (convert(sfd) == 5000) return -3000;
presence(sfd);
writeByte(sfd, 0xCC);
writeByte(sfd, 0xBE);
uint8_t data[9];
for (int i = 0; i < 9; i++) {
data[i] = readByte(sfd);
}
uint8_t crc = crc8(data, 9);
if (crc != 0) return -2000;
int t1 = data[0];
int t2 = data[1];
int16_t temp1 = (t2 << 8 | t1);
float temp = (float) temp1 / 16;
return temp;
}
Of course you can use the readBit and writeBit functions within other one-wire functions with minor modifications.
You can use the UART approach whenever a signaling protocol uses an initial start bit to signal that data bits follow using a fixed size “cell”. However, you cannot use the UART approach to the DHT22 temperature and humidity sensor because although it sends each bit with a start bit, the time to the next start bit varies.
Summary
The serial port is one of the oldest ways of connecting devices together, but it is still very much in use.
The serial protocol is asynchronous, but simple. A start bit gives the timing for the entire exchange.
There are many control lines once used with telephone equipment that are mostly ignored in computer use. Similarly, the original ±12V signaling has been mostly replaced by 5V, and even 3.3V, signaling.
The standard hardware that implements a serial connection is usually called a UART.
The Raspberry Pi Zero, 1, 2, and 3 each contain two UARTs and the Raspberry Pi 4 has both these plus four additional ones.
If you want to use a UART within your program, you first have to configure things so that Linux isn’t using it as command console.
The serial port is used in C programs as if it was a file, but there are are many ways to configure it using the ioctl function.
The ioctl function is too low-level for most applications and termios is a better way to do the job.
Termios can be used to set the protocol, baud rate, parity, etc and to determine how the software interacts with the UART, timeout etc.
You can use a UART to input data in blocking mode or non-blocking mode or something between the two.
The UART can be repurposed to work with any asynchronous signaling where the time to transmit a single bit is constant.
IBM is launching a new set of AI software engineering agents designed to autonomously resolve GitHub issues. The agents are being made available in an open-source licensing model.