Week 12 > Mechanical & Machine Design¶

For machine building week, Skylabworkshop + Fablab Winam + Innobiz-K Fablabs will work collaboratively on a common project. Using the MQTT network, machines will be built that will make rudimentary communication between the labs possible. The project will be called the ‘Hello-Hello Binary Communicators’.
In the movie ‘The Martian’, astronaut Mark Watney was stranded alone on the planet Mars. To communicate with distant earth, Mark utilized the rotating head of an abandoned rover to make conversations possible. We want to tap into this spirit of ingenuity…making do with whatever tech we have on hand…to build a meaningful device.
In his first communication attempt, he simply had the rotating head point left and right, indicating binary outcome answers of ‘Yes’ or ‘No’. With this simple, but slow means of communication, he was able to confirm communication capabilities between Earth and Mars.
Each of the 3 labs will also build Binary Communicators.”
Machine Design Components¶
Mechanism: Rack & Pinion
Actuation: Left/Right Motion
Automation: Arduino Programming > ESP32 (MCU), Motor Control (Stepper & Servo), MQTT Networking
Function: Indicate “Hello” & “Bye” messages
User Interface: MQTTX
GANTT Chart¶
Ethiopia:Innobiz-K > Tamrat Teklemarkos¶
Open & Close a window
Kenya:Fablab Winam > Charles Wangara¶


Forward & Reverse conveyance
Japan:Skylabworkshop > Rico Kanthatham¶
Left & Right Pointing Motion
The Skylabworkshop Binary Communicator

Rack & Pinion Mechanism driven by a Servo Motor
CAD Design

Circuit Diagram
Control Code
Code produced by ChatGPT with the prompt:
“An arduino program for an ESP32 microcontroller connected to a servo capable publishing temperature sensor data and receiving control instruction to move the servo motor smoothly to the 0 degree or 180 degree positions”
/*
ESP32 MQTT Servo Controller - Smooth Motion Version
Features:
- WiFi connection
- MQTT publish / subscribe
- Automatic MQTT reconnection
- JSON telemetry
- Remote servo command handling
- Last Will & Testament (online/offline status)
- Smooth non-blocking servo motion
MQTT commands to send to topic_command:
- Skylab_HELLO -> servo goes to 0
- Skylab_CENTER -> servo goes to 90
- Skylab_BYE -> servo goes to 180
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ESP32Servo.h>
// ==========================================
// 1. CONFIGURATION
// ==========================================
const char* ssid = "F660A-Y7NE-G";
const char* password = "6qetgqq9";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* mqtt_user = "";
const char* mqtt_pass = "";
const char* device_id = "skylab_esp32_servo";
// MQTT topics
const char* topic_telemetry = "esp32/skylab/data";
const char* topic_command = "esp32/skylab/cmd";
const char* topic_status = "esp32/skylab/status";
// ==========================================
// 2. GLOBAL OBJECTS & VARIABLES
// ==========================================
WiFiClient wifiClient;
PubSubClient client(wifiClient);
Servo myservo;
const int SERVO_PIN = 18;
// Current and target servo positions
int pos = 90;
int targetPos = 90;
// Motion timing
unsigned long lastServoStepTime = 0;
const unsigned long servoStepInterval = 15; // bigger = slower
const int servoStepSize = 1; // bigger = faster / less smooth
// Telemetry timing
unsigned long lastMsgTime = 0;
const unsigned long interval = 5000;
// ==========================================
// 3. WIFI SETUP
// ==========================================
void setupWiFi() {
Serial.println();
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println();
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Signal strength (RSSI): ");
Serial.println(WiFi.RSSI());
}
// ==========================================
// 4. MQTT CALLBACK
// ==========================================
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("]: ");
char messageBuffer[128];
unsigned int copyLength = length;
if (copyLength >= sizeof(messageBuffer)) {
copyLength = sizeof(messageBuffer) - 1;
}
for (unsigned int i = 0; i < copyLength; i++) {
messageBuffer[i] = (char)payload[i];
}
messageBuffer[copyLength] = '\0';
String msg = String(messageBuffer);
msg.trim();
Serial.println(msg);
if (strcmp(topic, topic_command) == 0) {
if (msg == "Skylab_HELLO") {
Serial.println("Target servo angle set to 0");
targetPos = 0;
client.publish(topic_telemetry, "{\"servo\":\"HELLO\",\"target_angle\":0}");
}
else if (msg == "Skylab_CENTER") {
Serial.println("Target servo angle set to 90");
targetPos = 90;
client.publish(topic_telemetry, "{\"servo\":\"CENTER\",\"target_angle\":90}");
}
else if (msg == "Skylab_BYE") {
Serial.println("Target servo angle set to 180");
targetPos = 180;
client.publish(topic_telemetry, "{\"servo\":\"BYE\",\"target_angle\":180}");
}
else {
client.publish(topic_telemetry, "{\"error\":\"unknown_command\"}");
}
}
}
// ==========================================
// 5. MQTT RECONNECT
// ==========================================
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(device_id, mqtt_user, mqtt_pass, topic_status, 1, true, "offline")) {
Serial.println("connected");
client.publish(topic_status, "online", true);
client.subscribe(topic_command);
Serial.print("Subscribed to: ");
Serial.println(topic_command);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" - retrying in 5 seconds");
delay(5000);
}
}
}
// ==========================================
// 6. SERVO SMOOTH MOTION
// ==========================================
void updateServoSmooth() {
unsigned long now = millis();
if (now - lastServoStepTime >= servoStepInterval) {
lastServoStepTime = now;
if (pos < targetPos) {
pos += servoStepSize;
if (pos > targetPos) {
pos = targetPos;
}
myservo.write(pos);
}
else if (pos > targetPos) {
pos -= servoStepSize;
if (pos < targetPos) {
pos = targetPos;
}
myservo.write(pos);
}
}
}
// ==========================================
// 7. SEND TELEMETRY
// ==========================================
void publishTelemetry() {
StaticJsonDocument<256> doc;
doc["device"] = device_id;
doc["uptime"] = millis() / 1000;
doc["wifi_rssi"] = WiFi.RSSI();
doc["servo_angle"] = pos;
doc["servo_target"] = targetPos;
doc["temp"] = random(60, 80);
char buffer[256];
serializeJson(doc, buffer);
Serial.print("Publishing data: ");
Serial.println(buffer);
client.publish(topic_telemetry, buffer);
}
// ==========================================
// 8. MAIN SETUP
// ==========================================
void setup() {
Serial.begin(115200);
delay(1500);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
myservo.setPeriodHertz(50);
myservo.attach(SERVO_PIN, 1000, 2000);
Serial.print("Servo attached to pin ");
Serial.println(SERVO_PIN);
pos = 90;
targetPos = 90;
myservo.write(pos);
delay(1000);
setupWiFi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
randomSeed(micros());
}
// ==========================================
// 9. MAIN LOOP
// ==========================================
void loop() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected. Reconnecting...");
setupWiFi();
}
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
updateServoSmooth();
unsigned long now = millis();
if (now - lastMsgTime >= interval) {
lastMsgTime = now;
publishTelemetry();
}
}
MQTT Networking¶
Utilizing the MQTT Networking Protocol and the MQTTX control application.
- Each lab subscribes to the other lab’s specific broadcast channel
- Other labs can send specific control messages to move another lab’s machine remotely
To control the Ehiopia machine…
- Subscriber Topic > esp32/tamrat/cmd
- Left Motion > “Skylab_HELLO”
- Right Motion > “Skylab_BYE”
To control the Kenya machine…
- Subscriber Topic > esp32/skylab/control
- Left Motion > “Forward”
- Right Motion > “Reverse”
To control the Japan machine…
- Subscriber Topic > esp32/skylab/cmd
- Left Motion > “Skylab_HELLO”
- Right Motion > “Skylab_BYE”