I am using an example code from the wiringPi library to read data from Arduino to Raspberry Pi through serial, it is displaying the data correctly with printf("%c", newChar); but I can't write the same data to a text file.
This is the whole file:
/*
Pi_Serial_test.cpp - SerialProtocol library - demo
Copyright (c) 2014 NicoHood. All right reserved.
Program to test serial communication
Compile with:
sudo gcc -o Pi_Serial_Test.o Pi_Serial_Test.cpp -lwiringPi -DRaspberryPi -pedantic -Wall
sudo ./Pi_Serial_Test.o
*/
// just that the Arduino IDE doesnt compile these files.
#ifdef RaspberryPi
//include system librarys
#include <stdio.h> //for printf
#include <stdint.h> //uint8_t definitions
#include <stdlib.h> //for exit(int);
#include <string.h> //for errno
#include <errno.h> //error output
//wiring Pi
#include <wiringPi.h>
#include <wiringSerial.h>
char device[]= "/dev/ttyAMA0";
// filedescriptor
int fd;
unsigned long baud = 9600;
unsigned long timeTemp=0;
unsigned long timeHum=0;
//unsigned long timeLight=0;
//unsigned long timeMotion=0;
//prototypes
int main(void);
void loop(void);
void setup(void);
void setup(){
printf("%s \n", "Raspberry Startup!");
fflush(stdout);
//get filedescriptor
if ((fd = serialOpen (device, baud)) < 0){
fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
exit(1); //error
}
//setup GPIO in wiringPi mode
if (wiringPiSetup () == -1){
fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
exit(1); //error
}
}
void loop() {
// Temperature every 3 seconds
if(millis()-timeTemp>=3000){
serialPuts (fd, "05\n");
// you can also write data from 0-255
// 65 is in ASCII 'A'
//serialPutchar (fd, 5);
timeTemp=millis();
}
// read signal
if(serialDataAvail (fd)){
char newChar = serialGetchar (fd);
FILE * writeTemp = fopen("temp.txt", "w");
printf("%c", newChar);
fputc(newChar, writeTemp);
fflush(stdout);
fclose(writeTemp);
}
// Humidity every 4 seconds
if(millis()-timeHum>=4000){
serialPuts (fd, "06\n");
// you can also write data from 0-255
// 65 is in ASCII 'A'
//serialPutchar (fd, 5);
timeHum=millis();
}
// read signal
if(serialDataAvail (fd)){
char newChar = serialGetchar (fd);
//printf("received from ardiono \n");
printf("%c", newChar);
fflush(stdout);
}
}
// main function for normal c++ programs on Raspberry
int main(){
setup();
while(1) loop();
return 0;
}
#endif //#ifdef RaspberryPi
I've tried different commands, but I'm constantly getting errors for invalid conversions from const char* to char or to FILE.
I just need to write the data from printf("%c", newChar); in a file.
In the line fputc(&newChar, writeTemp);, you're taking a pointer to your character, converting it to int, then writing that to your file. You should just write your character; something like fputc(newChar, writeTemp);. Or fprintf(writeTemp, "%c", newchar); if you prefer printf.
fputc takes a int, not an address. The problem that you are having is because you are passing the address of newChar. change the code to
fputc(newValue, tempFile);
and you should be good to go.
Good luck :)
If you want a printf-like function, use fprintf.
To write your output to a file, you would write
fprintf(writeTemp, "%c", newChar);
Also, the line
char value = printf("%c", newChar);
does not make sense, since value is declared as a char but is assigned the return status from printf.
As another reply points out, the arguments to fputc() are also wrong. You can write
fputc(newChar, writeTemp);
Using append instead of write somehow fixed the problem:
FILE * writeTemp = fopen("temp.txt", "a");
I also had to remove the second request:
/*
// Humidity every 3 seconds
if(millis()-timeHum>=3000){
serialPuts (fd, "06\n");
timeHum=millis();
}
// read signal
if(serialDataAvail (fd)){
char humChar = serialGetchar (fd);
printf("%c", humChar);
fflush(stdout);
}
*/
because it was interfering with the data for some reason, even with different char variable.
Thank you for the answers.
Related
Probably a silly question, with read and other functions you can specify the number of bytes you want to read, however when reading from stdin I find that I can only type 1024 characters in the prompt, if I type the 1025 character, it's not written and if I want the line to be read (pressing ENTER key) I need to remove the 1024 character in order to leave space for '\n' I suppose. This occurs only in my c program not the shell so what's causing this restriction?
#include <unistd.h>
#include <stdio.h>
int main() {
char buf[2048];
int c;
c = read(fileno(stdin), &buf, sizeof(buf));
printf("%s\n", buf);
return 0;
}
Transferring select comments to form an answer.
General diagnosis
This is a property of the terminal driver on your system, rather than of the program or the C library. Modern shells such as Bash don't read a single line; they read characters as they become available using non-canonical input. See also Canonical vs non-canonical terminal input.
Barmar noted:
Note that read() doesn't add a null terminator to the input that it reads, but printf() expects a null-terminated string.
Instead of adding a null terminator, you could tell printf() how many characters to print:
printf("%.*s\n", c, buf);
That is, however, tangential to the question of how to get a long line of input.
If you use an open source o/s, you can modify the terminal driver source code and recompile your kernel to allow you to type more than 1 KiB on a single line, but anything much short of that isn't going to work. The terminal driver imposes a limit; you have to change the terminal driver to change that limit. If you're on Linux, you can poke around the /proc file system to see if there's a dynamic configuration parameter you can change (so you don't have to recompile the kernel, but you do have to alter the settings of the terminal driver); I've not heard of that being possible.
The limit can be a nuisance if you copy'n'paste more than 1 KiB of text with no newlines in it from a browser and want to paste it into a file on your system. Use a program such as Vim to manage it — it puts the terminal into a non-canonical mode and therefore doesn't run into the limit.
Using POSIX termios to slurp input from a terminal
If you want a program to read from a terminal without the line lengths (but also with line editing such as erase or kill processing), then you could consider this program — slurp:
/*
#(#)File: $RCSfile: slurp.c,v $
#(#)Version: $Revision: 1.3 $
#(#)Last changed: $Date: 2018/10/28 17:14:24 $
#(#)Purpose: Put terminal into non-canonical mode to slurp input
#(#)Author: J Leffler
*/
/*TABSTOP=4*/
#include "posixver.h"
#include "stderr.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
static const char optstr[] = "a:ho:V";
static const char usestr[] = "[-hV][-a output | -o output]";
static const char hlpstr[] =
" -a output Append to named file (creating it if necessary)\n"
" -h Print this help message and exit\n"
" -o output Output to named file (truncating it if it exists)\n"
" -V Print version information and exit\n"
;
static struct termios saved = { 0 };
static bool sigint_enabled = false;
static bool sigquit_enabled = false;
static bool slurping = false;
static void reset_termios(void);
static void set_non_canonical(void);
static void sig_handler(int signum);
static void set_signal_handling(void);
static void slurp(int ofd, const char *filename);
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_slurp_c[];
const char jlss_id_slurp_c[] = "#(#)$Id: slurp.c,v 1.3 2018/10/28 17:14:24 jonathanleffler Exp $";
#endif /* lint */
int main(int argc, char **argv)
{
const char *filename = "standard output";
int ofd = STDOUT_FILENO;
int oflag = 0;
err_setarg0(argv[0]);
int opt;
while ((opt = getopt(argc, argv, optstr)) != -1)
{
switch (opt)
{
case 'h':
err_help(usestr, hlpstr);
/*NOTREACHED*/
case 'o':
case 'a':
if (ofd != STDOUT_FILENO)
{
err_remark("the -a and -o flags are mutually exclusive\n");
err_usage(usestr);
}
oflag = (opt == 'o') ? O_TRUNC : O_APPEND;
if ((ofd = open(optarg, O_WRONLY | O_CREAT | oflag, 0644)) < 0)
err_syserr("failed to open file %s for writing: ", optarg);
filename = optarg;
break;
case 'V':
err_version("PROG", &"#(#)$Revision: 1.3 $ ($Date: 2018/10/28 17:14:24 $)"[4]);
/*NOTREACHED*/
default:
err_usage(usestr);
/*NOTREACHED*/
}
}
if (optind != argc)
{
err_remark("unexpected file name options (first is '%s')\n", argv[optind]);
err_usage(usestr);
}
set_non_canonical();
if (slurping)
set_signal_handling();
slurp(ofd, filename);
return 0;
}
static void reset_termios(void)
{
tcsetattr(STDIN_FILENO, 0, &saved);
}
static void set_non_canonical(void)
{
if (tcgetattr(STDIN_FILENO, &saved) == 0)
{
struct termios modified = saved;
atexit(reset_termios);
/*
** On macOS 10.14 (at least), if you don't reset ISIG, the
** signal characters are not transferred to the program, so
** you can't detect those signals. With ICANON reset, they
** don't generate the signal either. The code does not try
** to handle the suspend (^Z) key specially, nor any other
** keys than EOF, INTR, QUIT.
*/
modified.c_lflag &= ~(ICANON | ISIG);
modified.c_cc[VMIN] = 1;
modified.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &modified);
slurping = true;
}
}
static void sig_handler(int signum)
{
reset_termios();
_exit(128 + signum);
}
/* Almost worth a data structure and a loop, but not quite */
static void set_signal_handling(void)
{
/* Simulate SIGINT and SIGQUIT */
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
{
(void)signal(SIGINT, sig_handler);
sigint_enabled = true;
}
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
{
(void)signal(SIGQUIT, sig_handler);
sigquit_enabled = true;
}
/* Have program terminate when sent normal signals */
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
(void)signal(SIGHUP, sig_handler);
if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
(void)signal(SIGTERM, sig_handler);
if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
(void)signal(SIGPIPE, sig_handler);
}
static void slurp(int ofd, const char *filename)
{
char buffer[4096];
int nbytes;
while ((nbytes = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0)
{
/* Simulate EOF and interrupt and quit signals */
if (nbytes == 1 && slurping)
{
if (buffer[0] == saved.c_cc[VEOF])
break;
if (sigint_enabled && buffer[0] == saved.c_cc[VINTR])
exit(128 + SIGINT);
if (sigquit_enabled && buffer[0] == saved.c_cc[VQUIT])
exit(128 + SIGQUIT);
}
if (write(ofd, buffer, nbytes) != nbytes)
err_syserr("failed to write %d bytes to %s: ", nbytes, filename);
}
}
The library code used is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c, stderr.h and posixver.h in the libsoq sub-directory.
This deals with most of the traps for the unwary. It does its best to reset the terminal back to the initial ('known good') state when it exits. It does simulate EOF, interrupt and quit keyboard signals, but it does not simulate regular terminal processing such as erase or kill.
It doesn't make sense to use this when the standard input is not a terminal, but the code should handle that OK too (it simply does normal reads). You can send the output to standard output (default) or to a file (-o file to create or truncate a file, -a file to append or create a file).
I am using this source code to read from the serial port of a linux machine. I am able to read from the port, but all of the values are in ascii gibberish ( i am reading the input from an xbox controller). I know I am sending it correctly, i.e. i can see on my side I am sending -128 - 127 as a char, but when I am converting it on my linux machine using atoi its returning 0, or when I try to cast the data to int it returns -48 , equivalent to 0 in ascii.
Is there a way for me to convert the incoming ascii into a readable integer like 64 or -114? I appreciate any help, thank you.
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
#include "rs232.h"
int main()
{
int i, n,
cport_nr=0, /* /dev/ttyS0 (COM1 on windows) */
bdrate=9600; /* 9600 baud */
unsigned char buf[4096];
char mode[]={'8','N','1',0};
if(RS232_OpenComport(cport_nr, bdrate, mode))
{
printf("Can not open comport\n");
return(0);
}
while(1)
{
n = RS232_PollComport(cport_nr, buf, 4095);
if(n > 0)
{
buf[n] = 0; /* always put a "null" at the end of a string! */
for(i=0; i < n; i++)
{
if(buf[i] < 32) /* replace unreadable control-codes by dots */
{
buf[i] = '.';
}
}
printf("received %i bytes: %s\n", n, (char *)buf);
}
#ifdef _WIN32
Sleep(100);
#else
usleep(100000); /* sleep for 100 milliSeconds */
#endif
}
return(0);
}
I have a code which runs bc thru popen(). I can intercept the calculator's output and prepend it with "Output=" text. But how can I intercept what user's is writing to bc?
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *in;
char buff[512];
if(!(in = popen("bc", "r"))){
exit(1);
}
while(fgets(buff, sizeof(buff), in)!=NULL){
printf("Output = %s", buff);
}
pclose(in);
return 0;
}
You can combine bc and echo with a pipe: echo '12*4' | bc
Example typing 12*4:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
FILE *in;
char buff[512];
char cmd[512];
while (fgets(buff, sizeof(buff), stdin)!=NULL){
strcpy(cmd, "echo '");
strcat(cmd, buff);
strcat(cmd, "' | bc");
if(!(in = popen(cmd, "r"))){
exit(1);
}
fgets(buff, sizeof(buff), in);
printf("output:%s", buff);
}
pclose(in);
return 0;
}
Output:
david#debian:~$ ./demo
12*4
output:48
You need to use pipe() and fork/exec(). However, manual piping is quite complex:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
int write_pipe[2], read_pipe[2];
pipe(read_pipe); pipe(write_pipe);
#define PARENT_READ read_pipe[0]
#define CHILD_WRITE read_pipe[1]
#define CHILD_READ write_pipe[0]
#define PARENT_WRITE write_pipe[1]
int child = fork();
if (child == 0) { /* in child */
close(PARENT_WRITE);
close(PARENT_READ);
dup2(CHILD_READ, 0); close(CHILD_READ);
dup2(CHILD_WRITE, 1); close(CHILD_WRITE);
execl("/usr/bin/bc", "/usr/bin/bc");
} else { /* in parent */
close(CHILD_READ);
close(CHILD_WRITE);
write(PARENT_WRITE, "2+3\n", 4);
char buff[512];
int output_len=read(PARENT_READ, buff, sizeof(buff));
write(1, buff, output_len);
close(PARENT_READ);
}
return 0;
}
What you're looking to do is to start a subprocess, then simultaneously:
When activity occurs on standard input, execute some function on that input before passing it to the subprocess.
When activity occurs on the subprocess output, execute some function on that output before passing it to standard output.
The system call that allows you to wait for activity on two handles is called poll, but before we do that, we need to create the handles and start the subprocess:
int a[2], b[2];
if(pipe(a)==-1)abort(); // for communicating with subprocess input
if(pipe(b)==-1)abort(); // for communicating with subprocess output
switch(fork()) {
case -1: abort();
case 0: dup2(a[0],0), dup2(b[1],1), execlp("/usr/bin/bc", "bc", 0); exit(1);
};
Note how pipe works: Data written to fildes[1] appears on (i.e., can be read from) fildes[0]. This means we want to read from the standard output of our subprocess, b[0] and write to the standard input of our subprocess a[1].
Before we do that, we can use the poll instruction to wait for activity on either standard input (fd #0), or the subprocess output (b[0]):
for(;;) {
struct pollfd p[2]={0};
p[0].fd = 0; p[1].fd = b[0];
p[0].events = p[1].events = POLLIN;
while (poll(p,2,-1) <= 0);
At this point, there is activity on at least one of these file descriptors. You can see which one by examining the .revents member.
if(p[0].revents & POLLIN) {
r = read(0, buffer, sizeof(buffer));
write(a[1], buffer, r); // check for errors, or perhaps modify buffer
}
if(p[1].revents & POLLIN) {
r = read(b[0], buffer, sizeof(buffer));
write(1, buffer, r); // check for errors, or perhaps modify buffer
}
Note especially we use the opposite member a[1] and b[0] from the member we dup2'd onto the subprocesses standard input (0) and standard output (1).
At this point you can loop back up to poll again:
}
Disconnects (like EOF, program crash, etc) will be presented as read() returning 0, so watch carefully for this case, and break; out of the loop if so desired.
I am trying to write 4 bytes to spi eeprom on linux and then read 4 bytes (the last is the important one, I'm working with zybo board), I did all for spi eeprom detection and now I have:
/sys/bus/spi/devices/spi32766.0/eeprom
In this link: Read and write to spi eeprom... Klaus says that is possible to do this using eeprom as a character file, but in this link: How to read data... Sawdust says that this is no possible because this kind of driver are platform driver.
I tried treating eeprom as a character file in C, but the obtained data are incoherent (I did a test in bare-metal code and the SPI device works), maybe because I don't know how data has to be send, Could somebody explain me with a piece of code, how should I read/write to SPI EEPROM?.
Thanks a lot :)
A summary of my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int readData(FILE* fp);
int main(int argc, char *argv[]) {
FILE* fp;
char ch = 'a';
while (1) {
fp = fopen("/sys/bus/spi/devices/spi32766.0/eeprom", "r+");
if (fp == NULL) {
printf("Cannot open /sys/bus/spi/devices/spi32766.0/eeprom for write\n");
return -1;
}
printf("Sending data\n");
fputs("\x01\x02\x03\x04", fp);//or fputs(four bytes, fp)
while (readData(fp) == 0) {
sleep(1);
}
printf("End\n");
fclose(fp);
sleep(5);
}
return EXIT_SUCCESS;
}
int readData(FILE* fp) {
int c = fgetc(fp);
int retorno = (feof(fp) == NULL);
printf("Char: %c\n", c);
printf("Int: %d\n", c);
return retorno;
}
I have got a task of sending hexadecimal data to my COMPORT in linux. I have written this simple C code, but it sends only a decimal number. Can anyone help me in sending an hexadecimal bit.
Here is the code I have written
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
int number,n;
void main(void){
open_port();
}
int open_port(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyACM0 - ");
}
else{
printf("Port Opened successfully\n");
number = 1;
while(number!=55){
scanf("%d",&number);
n = write(fd, "ATZ\r", number);
if (n < 0)
fputs("write() of 4 bytes failed!\n", stderr);
}
}
return (fd);
}
Please help
Thanks in advance :) :)
write is defined as:
ssize_t write(int fd, const void *buf, size_t count);
That is, it sends count bytes to fd from buf. In your case, the data is always the string "AZTR\r", plus undefined data after that (if count is > 5). Your program sends neither hexadecimal nor decimal data.
Do you want to send binary data or a string of hexadecimal characters?
For option one, you can use: write(fd, somebuffer, len);, where some buffer is a pointer to any set of bytes (including ints, etc).
For option two, first convert your data to a hexadecimal string using sprintf with %02X as the format string, then proceed to write that data to the port.
There are several problems with the code:
The text read from the console is interpreted as decimal ("%d"); if you want it to be interpreted as hexadecimal, use "%x".
The write() is pathological. The third argument is the number of bytes to write, not the value. It should be either
n = write (fd, "ATZ\r", 4); // there are 4 bytes to write to init the modem
or
char buf[10];
n = sprintf (buf, "%x", number); // convert to hex
n = write (fd, buf, n); // send hex number out port
This function will take a hex string, and convert it to binary, which is what you want to actually send. the hex representation is for humans to be able to understand what is being sent, but whatever device you are communicating with, will probably need the actual binary values.
// Converts a hex representation to binary using strtol()
unsigned char *unhex(char *src) {
unsigned char *out = malloc(strlen(src)/2);
char buf[3] = {0};
unsigned char *dst = out;
while (*src) {
buf[0] = src[0];
buf[1] = src[1];
*dst = strtol(buf, 0, 16);
dst++; src += 2;
}
return out;
}