Shop OBEX P1 Docs P2 Docs Learn Events
Indentify problem first, threads, then ... — Parallax Forums

Indentify problem first, threads, then ...

RsadeikaRsadeika Posts: 3,837
edited 2014-02-16 06:37 in Propeller 1
Below is a C program that I developed for using the Sensirion sht11 module on my Raspberry Pi, the Sensirion code was already available, in other words not my code. The program works as expected, but the concern that I have is this: When I start a session with putty and I run the sht11 program, it works as expected. Leaving the program running, I start another putty session and I start running another instance of sht11, the program starts as expected, but when I request 'temp' or 'humid', all I get are zero's instead of an actual temperature and humidity. Am I missing something about my understanding of how threads work? Why isn't the second instance of sht11 not accessing the sht11 thread?

I have developed a C program for using the Sensirion sht11 on my DNA-RTC board, which works as expected, but because I am using COGs, I am almost out of memory for some needed expansion for the program. So, I am thinking about re-writing the program to use threads instead of COGs, and therefore able to use the extra memory that is available. Because this program is using an XBee module, I am wondering if I will run into the same problem as mentioned above, when I have more than one XBee trying to access and run the program. I know that when I use COGs, I do not have a problem with multiple instances of accessing a COG, but will I have it with threads?

This is as good of an example that I could think of for developing a program in C, except for the code differences in accessing the Sensrion module, all the rest of the code could be a cut/paste session. Having said that, I am not so sure that the use of threads would result in the same kind of outcome when used on the Propeller. It seems like on the Raspberry Pi, a thread is much like a COG, except for the multiple instances problem that I am experiencing. On the Propeller, I am not sure that a thread acts the same way. So, there is that porting problem, but the rest is cut/paste. Anybody have a similar problem, and you got it resolved? Or how would I resolve the multiple instances of thread?

Ray


/* sht11.c */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <bcm2835.h>
#include "RPi_SHT1x.h"

#define PIN RPI_GPIO_P1_11

FILE *tp;

static volatile float Gnewt;
static volatile float Gtemp_val;
static volatile float Ghumi_val;

static volatile int starthour;
static volatile int stophour;

static volatile int SST;
static volatile int TlT;

void *sht11Thread(void *arg)
{
	unsigned char noError = 1;
	value humi_val, temp_val;

	while(1)
	{
		/* Set up the SHT1x Data and Clock pins.*/
		SHT1x_InitPins();
		/* Reset the SHT1x.*/
		SHT1x_Reset();
		/* Request Temperature measurment.*/
		noError = SHT1x_Measure_Start(SHT1xMeaT );
		if(!noError) return;
		/* Read Temperature measurement.*/
		noError = SHT1x_Get_Measure_Value((unsigned short int*)&temp_val.i);
		if(!noError) return;
		/* Request Humidity Measurement.*/
		noError = SHT1x_Measure_Start(SHT1xMeaRh);
		if(!noError) return;
		/* Read Humidity measurement.*/
		noError = SHT1x_Get_Measure_Value((unsigned short int*)&humi_val.i);
		if(!noError) return;
		/* Convert integers to float and calculate true values.*/
		temp_val.f = (float)temp_val.i;
		humi_val.f = (float)humi_val.i;
		/* Calculate F from given Celsius measurement.*/
		SHT1x_Calc(&humi_val.f, &temp_val.f);
		float newt = ((temp_val.f*1.8)+30.60);  // C to F conversion.
		Gnewt = newt;
		Gtemp_val = temp_val.f;
		Ghumi_val = humi_val.f;
		usleep(250000);
	}
	pthread_exit(NULL);
}

void *loggerThread(void *arg)
{
	SST = 0;
	char datebuff[16];
	char timebuff[16];
	char startbuff[16];
	char stopbuff[16];
	time_t now = 0;
	
	now = time(&now);
	strftime(datebuff,16,"%b %d, %Y",localtime(&now));
	tp = fopen("testlog.txt","a");
	fprintf(tp,"%s\n",datebuff);
	fclose(tp);

	while(1)
	{
		now = time(&now);
		strftime(timebuff,16,"%T",localtime(&now));
		if(SST == 1)
		{
		
			tp = fopen("testlog.txt","a");
			fprintf(tp,"%s",timebuff);
			fprintf(tp," T:%0.2f F %0.2f C",Gnewt,Gtemp_val);
			fprintf(tp," H:%0.2f %%\n",Ghumi_val);
			fclose(tp);
			sleep(60);
		}
		else
		{
			usleep(500000);
		}
	}
	pthread_exit(NULL);
}

void *TloggerThread(void *arg)
{
	

	while(1)
	{
		if(TlT == 1)
		{
			printf("%d\n",starthour);
			printf("%d",stophour);
			TlT = 0;
		}
		else
		{
			usleep(250000);
		}
	}
	
	pthread_exit(NULL);
}

void menu()
{
	printf("Menu: quit, temp, timeron\n");
	printf("      help, humid,timeroff\n");
}

void set_Timer()
{
	char startH[1];
	char stopH[1];

	printf("Start hour: ");
	gets(startH);
	starthour = atoi(startH); // Convert str to int
	printf("\nStop hour: ");
	gets(stopH);
	stophour = atoi(stopH);  // Convert str to int
	TlT = 1;
}

void set_Timer_on()
{
	SST = 1;
}

void set_Timer_off()
{
	SST = 0;
}

int main()
{
	char inBuff[40];

	if(!bcm2835_init())
		return 0;

	bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
	bcm2835_gpio_write(PIN,HIGH);

	pthread_t threads[5];

	pthread_create(&threads[1],NULL,sht11Thread,NULL);
	pthread_create(&threads[2],NULL,loggerThread,NULL);
	pthread_create(&threads[3],NULL,TloggerThread,NULL);
	sleep(2);

	printf("This is the sht11 program.\n");

	while(1)
	{
		printf("> ");
		gets(inBuff);
		if(!strcmp(inBuff,"quit")) break;
		else if(!strcmp(inBuff, "temp"))
		{
			printf("Temperature: %0.2f F %0.2f C\n",Gnewt,Gtemp_val);
		}
		else if(!strcmp(inBuff, "humid"))
		{
			printf("Humidity:    %0.2f %%\n",Ghumi_val);
		}
		else if(!strcmp(inBuff,"help"))
		{
			menu();
		}
		else if(!strcmp(inBuff,"timer"))
		{
			set_Timer();
		}
		else if(!strcmp(inBuff,"timeron"))
		{
			set_Timer_on();
		}
		else if(!strcmp(inBuff,"timeroff"))
		{
			set_Timer_off();
		}
		else
		{
			printf("?\n");
		}

		//sleep(1);
	}
	printf("Good Bye.\n");
	bcm2835_gpio_write(PIN, LOW);
	return 0;
}

Comments

  • jazzedjazzed Posts: 11,803
    edited 2014-02-15 12:10
    Ray,

    As I mentioned a few years ago, threading is an advanced concept where more rules must be followed. Globals are not thread safe for example. Typically, if a thread never writes to a global, you're Ok. If you have to use a global and write to it, then the idea of mutex must be used. A mutex allows locking access to data so that only one thread at a time can access it. That is, a thread should have mutually exclusive (mut-ex) access. Another issue is access to the hardware. Only one thread at a time can access the hardware until the required operation is completed. Then there is the idea of yielding. If your threads don't yield properly, the program can get hopelessly lost.
  • Heater.Heater. Posts: 21,230
    edited 2014-02-15 12:42
    Briefly looking at your code I'm not sure why you want to use threads.

    All your threads so stuff sequentially and then sleep a bit. Looks like this could all be done from one main loop.
  • RsadeikaRsadeika Posts: 3,837
    edited 2014-02-15 13:40
    Briefly looking at your code I'm not sure why you want to use threads.
    When I first developed this program I did not use any threads, I just had the sht11 thread as a function, and it was called when 'temp' and 'humid' were requested. Then I decided to add a logging task, but I wanted to still be able to have the main program still be accessible, meaning while the logging program was getting the temp and humidity data I was still able to request a temp and humidity from the main program at any time or have any other commands still functioning. At this point it woks as expected, except that only one instance can be achieved, and that is why I am curious as to why that is occurring.

    In the brief descriptions that I have seen of threading, especially when used on a PC, or in my case the Raspberry Pi, a thread is supposed to work almost like a COG, or at least that is my interpretation. And if the system happens to have multiple cores, the thread uses a core, not sure how that would be accomplished. Yes, I know this is an advanced topic, but I am here and I am trying to figure things out. If my reading about the thread using a core, when available, is correct, then why doesn't it use a COG when threading is run on the Propeller?

    Because there were some discussions, in another thread, about the porting capabilities of C, I thought this might be a good exercise in porting the Raspberry Pi sht11 version, with threads, to my DNA-RTC board, too see how that really works. In theory, as discussed here and at other places, it all sounds so great, well I thought maybe I would test it out for real, maybe separate fact from fiction, in the discussions.

    So far I have been using the internet to find some answers, but the discussions all revolve around C++ examples or problems, I am still having trouble with C, let alone trying to figure out how it is supposed to work in other languages. Now the part about one thread instance only can access the hardware aspect sounds like I may not be able to achieve what I would like to do, oh well, I will just keep digging.

    Ray
  • Heater.Heater. Posts: 21,230
    edited 2014-02-15 13:57
    Well, clearly if you have two or more threads hammering on the same memory area or I/O pins at the same time things are going to get very messed up.
    Access to resources, memory or hardware, needs to be coordinated between threads. Using mutexes is the common approach all though it can be done with just flags in simple cases of a single producer of data and a single consumer.

    If you are using pthreads you should assume that you have this problem, no matter if the threads run on two COGs or one.

    Others will have to suggest how to get pthreads spread around COGs or kept to a single COG.
  • jazzedjazzed Posts: 11,803
    edited 2014-02-15 14:26
    Ray,

    Portability of C can only be judged by code designed to work properly by someone who understands the issues of programming.
  • Heater.Heater. Posts: 21,230
    edited 2014-02-15 15:00
    jazzed,
    Portability of C can only be judged by code designed to work properly by someone who understands the issues of programming.
    I'm having a real hard time parsing any meaning out of that.

    I normally judge portability by trying to get the code compiled for the new target platform and running it there. Hopefully with some tests that can be applied to it,

    As an example I'm sure Roy Eltham knows how to make portable code. Even so openspin is not portable. It won't run correctly on architectures that are fussy about aligned memory accesses.
  • RsadeikaRsadeika Posts: 3,837
    edited 2014-02-16 04:23
    Mutex's it is then, maybe some other unforeseen problem will arise from this. Since this will be an experiment, and my boards are in use, I have to come up with an experiment that would be valid for a real port of code using some other boards. I have a Raspberry Pi Model A, and of course some other Propeller boards. Now I have to figure out an experiment that would use some hardware which would be available on both boards that could result in a comparable result. I guess I could use an LED, available on both boards, but can I come up with an experiment that would use a thread(s), mutex(s), and show the real portability of code between different hardware, while using the LED?

    I found an internet sight, POSIX Thread Programming, which seems to have a good explanation of how to use a mutex, but I guess I will not be sure until I really get into the subject. Any other suggestions would be appreciated.

    Ray
  • Heater.Heater. Posts: 21,230
    edited 2014-02-16 05:05
    Ray,

    If you want to make an experiment with threads and mutual exclusion using some hardware interface what about outputting on a serial port? A serial port on the Prop is easy enough and the Pi has one on the GPIO headers if you don't want to use a USB/Serial adapter.

    Here would be a plan.

    Create two or more threads.

    Have them output lines of human readable text to the serial port that you can observe using whatever terminal program on your PC.

    Have that text be changing all the time.

    The challenge is that complete lines must be preserved intact. Lines from different threads can be received in a jumbled order but each line must be complete and correct.

    Use mutexes to coordinate access to the serial port resource. Get the mutex, send a line, release the mutex.

    To make things more interesting, make a program on the PC that echoes those lines back to the Prop/Raspi. Kind of like a command/response set up.

    Now the threads have to:

    1) Get the mutex,
    2) Send the "command" lines.
    3) Wait for and receive the echo reply.
    4) Check that it is correct
    5) Release the mutex.

    Now we have the framework of a shared hardware device, could be SPI memory or whatever.

    Of course you can skip the PC and have the Pi and Prop talk to each other.

    Use a LED at each end to indicate FAIL/ERROR of course:)
  • RsadeikaRsadeika Posts: 3,837
    edited 2014-02-16 05:21
    The big problem, for me, with this suggestion is that I do not have a serial C coded program. Now this would be a problem in a porting sense since on a Propeller you would have to use the built in serial port drivers that are available. Or are you suggesting using a specific C serial driver that would also be used on the Propeller and the Rasberry Pi. A few months back I did look into developing a program that would use termios.h but it went over my head very quickly. I guess what I would need is a basic serial program written in C, which I have not come across yet. Still thinking ...

    Ray
  • Heater.Heater. Posts: 21,230
    edited 2014-02-16 06:02
    You would use different serial port code on Prop and Pi. Although wrapped up behind some common function interface so that the "app" part of the experiment is the same in both cases.

    I'm sure propgcc has a means of using a serial port available in it's libs. Not sure what is what there, haven't checked for a long time.

    For the Pi I have a drop dead easy to use serial C code I can let you have. That will have to wait till Monday though.

    Or look at "Wiring Pi" which seems to have a lot of GPIO stuff all wrapped up for easy use. Perhaps even a serial port driver.
  • RsadeikaRsadeika Posts: 3,837
    edited 2014-02-16 06:37
    I found my last attempt at working with some serial code using termios.h, I think this worked, but I am not sure. What was really getting to me was this: "O_WRONLY | O_NOCTTY | O_NDELAY", I never did find an explanation of how to use it. So, the code below will have to be my starting point, before I start to add threads and mutex and stuff. Now I am wondering if the code below would work on the Propeller using PopGCC? After it was cleaned up of course.

    Now I have to prepare my RPi Model-A with the new whezzy OS, since it does not have a lan connection I will not be able to get anything off the internet. Since I have a GG PPUSB board available that is what I will use for the Propeller side. And since it does not have any XMM, I will be using the LMM and CMM modes, I think.

    Ray
    /* Cterm.c */
    #include <stdio.h>
    #include <error.h>
    //#include <stdlib.h>
    //#include <stdint.h>
    //#include <stdarg.h>
    //#include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <termios.h>
    
    int main()
    {
    	int fd = open("/dev/ttyUSB0",O_WRONLY | O_NOCTTY | O_NDELAY);
    	if(fd == -1)
    	{
    		perror("open_port: Unable to open /dev/ttyUSB0");
    	}
    
    	int z = write(fd,"temp\n",5);
    	if(z<0) fputs("write fail\n",stderr);
    	//int y = write(fd,"26",2);
    	//if(y<0) fputs("write fail\n",stderr);
    	fd = open("/dev/ttyUSB0", O_RDONLY | O_NOCTTY);
    	if(fd == -1) perror("open_port: Unable to open /dev/ttyUSB0");
    	sleep(1);
    	char a[32];
    	int n = read(fd,a,sizeof(a));
    	if(n<0) fputs("read failed.\n",stderr);
    	printf(" %s\n",a);
    	
    
    	close(fd);
    	return 0;
    }
    
    
Sign In or Register to comment.