1

I am trying to use a protothreading library found here

http://harteware.blogspot.com/2010/11/protothread-powerfull-library.html

to perform protothreading with my Arduino so I can "simultaneously" read from the HC-05 Bluetooth chip input to receive information from the master computer which I am connected to over Bluetooth while also processing and transmitting data from the sensors over Bluetooth.

My code looks like this

/*
    This module uses the L293D chip to modulate and power
    two DC motors controlling the wheels of the FryeBot
*/


#include <Time-1.5.0\Time.h>
#include <WiShield\pt.h>

/*L293D INPUT 7*/
const int YELLOW_GEAR_MOTOR_BACK = 8;

/*L293D INPUT 2*/
const int YELLOW_GEAR_MOTOR_FORWARD = 9;

/*L293D INPUT 10*/
const int RED_GEAR_MOTOR_BACK = 10;

/*L293D INPUT 15*/
const int RED_GEAR_MOTOR_FORWARD = 11;

/*
    Establish ratio os the rate of the faster slower gear
    to the faster gear to scale the duty cycle of the
    faster gear
*/
const float RED_GEAR_SPEED_TO_YELLOW = 0.97;

/*Constant for full duty cycle for analogWrite*/
const int FULL_ANALOG_MOTOR_SPEED = 255;

/* HC05 Setup Data */
const int pingPin = 7;
const int HC05PowerPin = 2;

/* Distance data */
long duration, cm;

/* Values for Bluetooth RX/TX */
char buf[25];

char current;
String messageHolder;
String message;

/* Protothreads */
static struct pt pt1, pt2;

void setup() {
    Serial.begin(9600);
    Serial1.begin(9600);

    /* add setup code here */
    pinMode(YELLOW_GEAR_MOTOR_FORWARD, OUTPUT);
    pinMode(YELLOW_GEAR_MOTOR_BACK, OUTPUT);
    pinMode(RED_GEAR_MOTOR_BACK, OUTPUT);
    pinMode(RED_GEAR_MOTOR_FORWARD, OUTPUT);
    pinMode(HC05PowerPin, OUTPUT);

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

}

static int protothreadCollectAndTransmitData(struct pt* pt, int interval) {
    static unsigned long timestamp = 0;

    PT_BEGIN(pt);

    while (1) {
        collectAndTransmitData();
        PT_WAIT_UNTIL(pt, millis() - timestamp > interval);
    }

    PT_END(pt);
}

void collectAndTransmitData() {
    digitalWrite(HC05PowerPin, HIGH);

    pinMode(pingPin, OUTPUT);
    digitalWrite(pingPin, LOW);
    delayMicroseconds(2);
    digitalWrite(pingPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(pingPin, LOW);

    pinMode(pingPin, INPUT);
    duration = pulseIn(pingPin, HIGH);

    cm = microsecondsToCentimeters(duration);

    Serial.print("TX: ");
    Serial.print(cm);
    Serial.println();

    if (message.indexOf("G") > 0) {

        analogWrite(YELLOW_GEAR_MOTOR_FORWARD, (int)(RED_GEAR_SPEED_TO_YELLOW * (float)FULL_ANALOG_MOTOR_SPEED));
        digitalWrite(YELLOW_GEAR_MOTOR_BACK, LOW);

        digitalWrite(RED_GEAR_MOTOR_BACK, LOW);
        digitalWrite(RED_GEAR_MOTOR_FORWARD, HIGH);
    }
    else if (message.indexOf("S") > 0) {
        digitalWrite(YELLOW_GEAR_MOTOR_FORWARD, 0);
        digitalWrite(YELLOW_GEAR_MOTOR_BACK, LOW);

        digitalWrite(RED_GEAR_MOTOR_BACK, LOW);
        digitalWrite(RED_GEAR_MOTOR_FORWARD, LOW);
    }

    /* Load the distance value into a buffer */
    String sendValue = String(cm);
    sendValue.toCharArray(buf, sizeof(buf));

    /* Write the buffer with a space after for parsing*/
    Serial1.write(buf);
    Serial1.write(' ');
}

long microsecondsToCentimeters(long microseconds) {
    // The speed of sound is 340 m/s or 29 microseconds per centimeter.
    // The ping travels out and back, so to find the distance of the
    // object we take half of the distance travelled.
    return microseconds / 29 / 2;
}

static int protothreadReadAndParseData(struct pt* pt, int interval) {
    static unsigned long timestamp = 0;

    PT_BEGIN(pt);

    while (1) {
        readAndParseData();
        PT_WAIT_UNTIL(pt, millis() - timestamp > interval);
    }

    PT_END(pt);
}

void readAndParseData() {
    Serial.println("HERE");
    current = Serial1.read();
    Serial.print("RX: ");
    Serial.print(current);
    Serial.println();

    if (current == ' ') {
        message = messageHolder;
        messageHolder = "";
        Serial.print("message: ");
        Serial.print(message);
        Serial.println();
    }
    else {
        messageHolder += current;
    }
}


void loop() {
    Serial.println("A");
    protothreadReadAndParseData(&pt2, 20); // by calling them infinitely
    Serial.println("B");
    protothreadCollectAndTransmitData(&pt1, 20); // schedule the two protothreads
}

The code prints the following in the console when executed.

A
HERE
RX: �
B
TX: 522
TX: 524
TX: 524
TX: 524
TX: 524
TX: 524

Then continues with TX: ### forever.

I am not sure why the code is not looping back to the first function or at least running it a second time. I am not very clear on exactly how to use this library or who maintains it. The docs on their Github page for their WiShield library which this pt.h is part of are not overly helpful. If you have any experience with other Arduino protothreading libraries let me know so I might try them out. I am not sure where I went wrong with this library in my implementation.

Here is the repo containing the package I am using

https://github.com/nicolaskruchten/arduino/tree/master/libraries/WiShield

1

In protothreadCollectAndTransmitData you have a timestamp variable that never gets updated:

static int protothreadCollectAndTransmitData(struct pt* pt, int interval) {
    static unsigned long timestamp = 0;   // <- This never changes
    ....

When the condition is true you will need to update timestamp = millis();.

static int protothreadCollectAndTransmitData(struct pt* pt, int interval) {
    static unsigned long timestamp = 0;

    PT_BEGIN(pt);

    while (1) {
        PT_WAIT_UNTIL(pt, millis() - timestamp > interval);
        // Updated. Move this here, else it ALWAYS gets called!
        collectAndTransmitData();
        timestamp = millis();   // <- Update timestamp for next time
    }

    PT_END(pt);
}

But to make sure that is correct, you should look at the macro:

#define PT_WAIT_UNTIL(pt, condition) \
do {                                 \
    LC_SET((pt)->lc);                \
    if(!(condition)) {               \
        return PT_WAITING;           \
    }                                \
} while(0)

(You can ignore the do/while - that's just there so you can use a semi-colon.) As you can see, when the condition is false a return is issued. So it looks like it is safe to do it this way.

Disclaimer: I've never used this threading library. I'm working off of the source code I found here: https://github.com/fernandomalmeida/protothreads-arduino/blob/master/pt.h

  • if you had to guess, what would you put on the millisecond argument in loop() to optimize performance for each of the threads? my baud for the serial is 9600, so i think it would make sense to try a 1 on that. Having done so, it looks like it only wants to run that thread. Barely ever touches the other one running at 20 ms – Jack Frye Sep 15 '17 at 14:53
  • does that lib work with arduino? – Jack Frye Sep 15 '17 at 15:09
  • Looks like I had the wrong link. Updated with what appears to be the Arduino port. – Johnny Mopp Sep 15 '17 at 15:15
  • The timeout for collectAndTransmitData should be whatever your sample rate is. How often do you need to check the sensor? For readAndParseData() I would add if (!Serial.available()) return; as the first line so the function never gets entered unless a message is waiting. – Johnny Mopp Sep 15 '17 at 15:17
  • it seems it will only run one thread continuously for a while then switch. I will try your library – Jack Frye Sep 15 '17 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.