9

I'm doing a project about controlling two sensors (ultrasonic and infrared), managing them with Arduino. The IR receiver has a filter system inside, so it receives at the frequency of 36 kHz. I use the module srf04 to handle the ultrasonic stuff. If I do a program which has to control only one sensor, it works. But I have to interpolate the two signals into one result. So I used protothreads! But it doesn't work... What's the error?

Here is the code:

#include <pt.h>

int iro = 8, iri = 4, us = 12, distanza, us_vcc = 13, ir_vcc = 7;
long durata;

static struct pt pt1, pt2, pt3;

static int irthread(struct pt *pt) {
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, 1>0);
    digitalWrite(iro, HIGH);
    delayMicroseconds(9);
    digitalWrite(iro, LOW);
    delayMicroseconds(9);
  }
  PT_END(pt);
}

static int usthread(struct pt *pt) {
  static unsigned long timer = 0;
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, millis() - timer > 200);
    timer = millis();
    pinMode(us, OUTPUT);
    digitalWrite(us, LOW);
    delayMicroseconds(5);
    digitalWrite(us, HIGH);
    delayMicroseconds(10);
    digitalWrite(us, LOW);
    pinMode(us, INPUT);
    durata = pulseIn(us, HIGH);
    distanza = durata/58;
  }
  PT_END(pt);
}

static int leggithread(struct pt *pt) {
  static unsigned long timer = 0;
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, millis() - timer > 200);
    timer = millis();
    Serial.print(distanza);
    Serial.print("cm ");
    if (digitalRead(iri) == LOW)
      Serial.println("ir si");
    else
      Serial.println("ir no");
  }
  PT_END(pt);
}

void setup() {
  pinMode(iro, OUTPUT);
  pinMode(iri, INPUT);
  pinMode(us_vcc, OUTPUT);
  digitalWrite(us_vcc, HIGH);
  pinMode(ir_vcc, OUTPUT);
  digitalWrite(ir_vcc, HIGH);
  Serial.begin(9600);

  PT_INIT(&pt1);
  PT_INIT(&pt2);
  PT_INIT(&pt3);
}

void loop() {
  irthread(&pt1);
  usthread(&pt2);
  leggithread(&pt3);
}

The single parts of code of each thread works.


Update

I solved my problem (eliminated irthread()) and the code is now like this:

#include <pt.h>

int iro = 8, iri = 4, us = 12, distanza, us_vcc = 13, ir_vcc = 7;
long durata;

static struct pt pt1, pt2;

static int usthread(struct pt *pt) {
  static unsigned long timer = 0;
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, millis() - timer > 200);
    timer = millis();
    pinMode(us, OUTPUT);
    digitalWrite(us, LOW);
    delayMicroseconds(5);
    digitalWrite(us, HIGH);
    delayMicroseconds(10);
    digitalWrite(us, LOW);
    pinMode(us, INPUT);
    durata = pulseIn(us, HIGH);
  }
  PT_END(pt);
}

static int leggithread(struct pt *pt) {
  static unsigned long timer = 0;
  PT_BEGIN(pt);
  while(1) {
    PT_WAIT_UNTIL(pt, millis() - timer > 200);
    timer = millis();
    distanza = durata/58;
    Serial.print(distanza);
    Serial.print("cm ");
    if(digitalRead(iri) == LOW)
      Serial.println("ir si");
    else
      Serial.println("ir no");
  }
  PT_END(pt);
}

void setup() {
  pinMode(iro, OUTPUT);
  tone(iro, 36000);
  pinMode(iri, INPUT);
  pinMode(us_vcc, OUTPUT);
  digitalWrite(us_vcc, HIGH);
  pinMode(ir_vcc, OUTPUT);
  digitalWrite(ir_vcc, HIGH);
  Serial.begin(9600);

  PT_INIT(&pt1);
  PT_INIT(&pt2);
}

void loop() {
  usthread(&pt1);
  leggithread(&pt2);
}

Now the problem is the ultrasonic sensor. If I control it in a single program without protothreads it can reach objects to a distance of 3 meters. Now even if I put something at 1 meter the "distanza" is 15 cm max. What is the error?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Antonio
  • 93
  • 1
  • 5

3 Answers3

9

In irthread() the second argument to macro PT_WAIT_UNTIL always evaluates to true:

PT_WAIT_UNTIL(pt, 1>0);

Thus the program will be stuck in irthread()'s infinite loop, because part of the result of macro PT_WAIT_UNTIL in this case is something like if(!(1>0)) return 0;; the statement return 0 is never called.


It works for usthread() and leggithread() as the second argument is false for the first 200 milliseconds and the variables are set up so it will be false again for another 200 milliseconds after being true for a single time.

Some background information is in How protothreads really work.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • ok, but if i put a condition that's not true at the begin in the PT_WAIT_UNTIL of irthread(), the frequency will be wrong. How can I do to have a frequency of 36 kHz without problems? – Antonio Jun 05 '11 at 09:01
  • @Antonio: Doesn't "tone(iro, 36000);" set up a timer that outputs a 36 kHz squareware to I/O pin 8? – Peter Mortensen Jun 05 '11 at 14:33
1

The timers in leggithread() and usthread() interferes with each other. They use the same variable, timer. When time is up, after about 200 milliseconds since last time, in, say leggithread(), the variable is reset. It means the condition in the other function, usthread() (that is called right after), will be false even though the condition there was about to be true. Thus at least another 200 milliseconds will pass before usthread() can do work (outputting a 10 microsecond pulse on port 12).

There is no guarantee that both functions will be called. If you are unlucky only one of them may be called if it is a deterministic system (driven from the same clock, the microcontroller's crystal).

It could be random which one is called or there could be some aliasing between several frequencies (for instance, one frequency represented by the number of executed instructions for each loop - that frequency will change when the program is changed).

If you want both leggithread() and usthread() doing work five times per second then they should each have an independent timer, using separate variables, for example, timer1 and timer2.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
-1

Why have you put while(1) in your function? Since 1 is always true -

    while(1) {
        // The code in it will repeat forever
    }
    // And the Arduino will never get here

Either you put a logic instead of 1 (like while(x > 10), while(task_finished)) or don't put your code in the while statement.


    static int usthread(struct pt *pt) {
        static unsigned long timer = 0;
        PT_BEGIN(pt);
        while(1) { // <<<<<<<<< Fault 1
            PT_WAIT_UNTIL(pt, millis() - timer > 200);

    PT_BEGIN(pt);
    while(1) { //<<<<<<<<< Fault 2
        PT_WAIT_UNTIL(pt, millis() - timer > 200);
        timer = millis();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rudra
  • 7
  • 3
  • i realised that the post is 4 years old from now but the answer can be useful to other people. – Rudra Feb 06 '16 at 11:54
  • 3
    Protothreads change the control flow in thread functions, which is why they're implemented as macros. [*How Protothreads Really Work*](http://dunkels.com/adam/pt/expansion.html) explains pretty well how they work. In short, there's a `return 0` statement inside that while loop. – KevinOrr Feb 08 '16 at 08:04
  • The macro `PT_WAIT_UNTIL` contains a `return` statement (executed depending on a condition) so what appears to be an infinite loop may not actually be an infinite loop. – Peter Mortensen Feb 29 '16 at 12:57