Skills Dev V: Working with Networking
Made by Xuan Peng, Yuhan Wu and Junke Zhao
Made by Xuan Peng, Yuhan Wu and Junke Zhao
A pair of connected devices passing trigger information to each other's servo.
Created: December 16th, 2023
The team explored the paired device exercise with a servo to signal a classic vibration of phones.
The argon and phone attached to servos are places in the order (A, B, C from the left to right), the calling sequence is C to B, B to A, A to C...
The serial monitor in the foreground belongs to argon B in the middle. It shows its work as both subscription and publish.
Servo myServo;
int potPin = A5;
int lastPos = -1;
int pairPos = -1;
bool allowDialInput = true;
// Flag to control dial input
unsigned long lastSubscriptionTime = 0;
const unsigned long dialInputTimeout = 2000;
// Time out to disable the dial at a period of time
void setup() {
Serial.begin(9600);
myServo.attach(A3);
Particle.variable("servoPos", myServo.read());
Particle.subscribe("spinServo", handleSpinServo);
}
void loop() {
int potReading = analogRead(potPin);
int mappedPos = map(potReading, 0, 4095, 20, 160);
if (abs(mappedPos - lastPos) > 20 && allowDialInput) {
Particle.publish("doPairedPublish", String(mappedPos));
// Publish the updated servo position to next device
// and print in serial monitor
Serial.print("Publish servo position: ");
Serial.println(mappedPos);
// Update the last known position
lastPos = mappedPos;
// Add a delay to avoid rapid updates
delay(500);
}
// Check if the timeout has passed, re-enable dial input
if (!allowDialInput && millis() - lastSubscriptionTime >= dialInputTimeout) {
allowDialInput = true;
Serial.println("Dial input re-enabled");
}
}
void handleSpinServo(const char *event, const char *data) {
int newPos = atoi(data);
allowDialInput = false;
myServo.write(newPos);
// Disable dial input when handling external subscription
// Set the servo position based on the subscribed value
pairPos = newPos;
// Record the paired position
Serial.print("Set servo position via subscribe to: ");
Serial.println(newPos);
// Print the updated servo position to the serial monitor
// Record the time of the subscription
lastSubscriptionTime = millis();
}
Click to Expand
Started with prototyping the paired devices that can rotate a same degree with the output of a servo, we initially made the two phones vibrate together when any button was pushed, but we wanted to make this action more interactive, so we chose knobs that allow the user to control the servo more to their heart's content to convey more information.
And then we add a dial to provide a more interactive reading for the project. During testing with the project we found that the reading of two dials on separate argon could trigger the servo to move back and forth, which is also a pretty intriguing pattern. If the mapped value of the dial match within a tolerance angle of 20, the two servo would be stable and static, but if the gap is bigger than that, they would send local reading value of the dial to each other and keep swapping that reading from the dial.
We thought that to control in terms of a loop of 3, it might be more effective if we only publish the pot reading to the subscription and avoid the duplication of a local dial reading as well as the reading from other devices.
Servo myServo;
int potPin = A5;
int lastPos = -1;
int pairPos = -1;
bool allowDialInput = true; // Flag to control dial input
unsigned long lastSubscriptionTime = 0;
const unsigned long dialInputTimeout = 2000;
void setup() {
Serial.begin(9600);
myServo.attach(A3);
Particle.variable("servoPos", myServo.read());
Particle.subscribe("spinServo", handleSpinServo);
}
void loop() {
int potReading = analogRead(potPin);
int mappedPos = map(potReading, 0, 4095, 20, 160);
// Only update the servo position if the potentiometer reading has changed significantly
if (abs(mappedPos - myServo.read()) > 20 && allowDialInput) {
myServo.write(mappedPos);
Serial.print("Set servo position to: ");
Serial.println(mappedPos);
Particle.publish("doPairedPublish", String(mappedPos));
Serial.print("Published new position: ");
Serial.println(mappedPos);
lastPos = mappedPos;
delay(500);
}
// Check if the timeout has passed, re-enable dial input if necessary
if (!allowDialInput && millis() - lastSubscriptionTime >= dialInputTimeout) {
allowDialInput = true;
Serial.println("Dial input re-enabled");
}
}
void handleSpinServo(const char *event, const char *data) {
// Convert the data to an integer representing the new servo position
int newPos = atoi(data);
// Disable dial input when handling external subscription
allowDialInput = false;
myServo.write(newPos);
// Record the paired position
pairPos = newPos;
Serial.print("Set servo position via subscribe to: ");
Serial.println(newPos);
lastSubscriptionTime = millis();
}
Click to Expand
As demonstrated in the video above, the sync function works in an interesting pattern by could cause the conflict in dial reading value. The potential solution besides eliminate the local control feature maybe more compelling if we use a slider or push button to control wether or not the data is going to be published or submitted to be used for the local servo. If possible, the exchange could be oriented by a single slider.
We really like the expression form of two old phones, which kind of brought us back to the time that phone was just used to communicate with each other, instead of taking time away from the social life we're supposed to have. For next steps we might consider adding one more sensor to convey more information using light or sound.
A hands-on introductory course exploring the Internet of Things and connected product experiences.
A pair of connected devices passing trigger information to each other's servo.