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 Answer 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 msJack Frye– Jack Frye2017年09月15日 14:53:11 +00:00Commented Sep 15, 2017 at 14:53
-
does that lib work with arduino?Jack Frye– Jack Frye2017年09月15日 15:09:12 +00:00Commented Sep 15, 2017 at 15:09
-
Looks like I had the wrong link. Updated with what appears to be the Arduino port.001– 0012017年09月15日 15:15:08 +00:00Commented Sep 15, 2017 at 15:15
-
The timeout for
collectAndTransmitData
should be whatever your sample rate is. How often do you need to check the sensor? ForreadAndParseData()
I would addif (!Serial.available()) return;
as the first line so the function never gets entered unless a message is waiting.001– 0012017年09月15日 15:17:51 +00:00Commented Sep 15, 2017 at 15:17 -
it seems it will only run one thread continuously for a while then switch. I will try your libraryJack Frye– Jack Frye2017年09月15日 16:14:26 +00:00Commented Sep 15, 2017 at 16:14
Explore related questions
See similar questions with these tags.