a scrappy gimbal that insults you in shakespearean english

feat: add blot.c

Changed files
+456
+456
blot.c
···
···
+
#include <Servo.h>
+
+
#define SPU 80 // 20 teeth pulley, 16 microstep, 2mm a tooth belt, 16*200 microsteps per turn == (16*200)/(20*2) == 80
+
#define PIN_SERVO D6
+
+
Servo servo;
+
+
typedef uint8_t (*CallbackFunction)(uint8_t*, int, uint8_t*);
+
+
struct EventCallback {
+
String event;
+
CallbackFunction callback;
+
};
+
+
+
float pos[] = {0, 0};
+
+
const int motor1StepPin = D10;
+
const int motor1DirPin = D9;
+
const int motor2StepPin = D8;
+
const int motor2DirPin = D7;
+
+
const int enablePin = D1;
+
+
void setup() {
+
servo.attach(PIN_SERVO);
+
+
Serial.begin(9600);
+
+
// on("light", onLight);
+
on("go", go);
+
on("servo", moveServo);
+
on("motorsOn", motorsOn);
+
on("motorsOff", motorsOff);
+
on("moveTowardsOrigin", moveTowardsOrigin);
+
on("setOrigin", setOrigin);
+
+
pinMode(motor1StepPin, OUTPUT);
+
pinMode(motor1DirPin, OUTPUT);
+
pinMode(motor2StepPin, OUTPUT);
+
pinMode(motor2DirPin, OUTPUT);
+
+
pinMode(enablePin, OUTPUT); // enable pin
+
+
pinMode(PIN_LED, OUTPUT);
+
+
startupIndicator(); // once startup is finished, indicate to user
+
}
+
+
void loop() {
+
readSerial();
+
}
+
+
void startupIndicator() {
+
// flash LED to indicate board is initialized
+
for (int i = 0; i < 3; i++) {
+
digitalWrite(PIN_LED, 0);
+
delay(200);
+
digitalWrite(PIN_LED, 1);
+
delay(200);
+
}
+
}
+
+
uint8_t onLight(uint8_t* payload, int length, uint8_t* reply) {
+
uint8_t value = payload[0];
+
+
// Serial.println("light it up");
+
+
digitalWrite(PIN_LED, value);
+
+
return 0;
+
}
+
+
uint8_t motorsOn(uint8_t* payload, int length, uint8_t* reply) {
+
digitalWrite(enablePin, 0);
+
return 0;
+
}
+
+
uint8_t motorsOff(uint8_t* payload, int length, uint8_t* reply) {
+
digitalWrite(enablePin, 1);
+
return 0;
+
}
+
+
uint8_t moveTowardsOrigin(uint8_t* payload, int length, uint8_t* reply) {
+
float x = pos[0];
+
float y = pos[1];
+
+
// this ternary may not be neccesary
+
goTo(
+
x + ( x < 0 ? 10 : -10),
+
y + ( y < 0 ? 10 : -10)
+
);
+
+
return 0;
+
}
+
+
uint8_t setOrigin(uint8_t* payload, int length, uint8_t* reply) {
+
pos[0] = 0;
+
pos[1] = 0;
+
+
return 0;
+
}
+
+
+
/* ------------------------------------------------------------ */
+
+
int bufferIndex = 0;
+
uint8_t msgBuffer[100];
+
+
void readSerial() {
+
while (Serial.available() > 0) {
+
uint8_t incoming = Serial.read();
+
+
msgBuffer[bufferIndex] = incoming;
+
+
if (incoming != 0) {
+
bufferIndex++;
+
continue; // Proceed to the next byte if current byte is not null
+
}
+
+
// Serial.print("RECEIVED: ");
+
// for (int i = 0; i < bufferIndex; i++) {
+
// Serial.print(msgBuffer[i]);
+
// Serial.print(", ");
+
// }
+
// Serial.println("DONE");
+
+
// Now we have a full message, perform COBS decoding
+
uint8_t decoded[bufferIndex];
+
cobs_decode(decoded, msgBuffer, bufferIndex);
+
+
// Serial.print("DECODED: ");
+
// for (int i = 0; i < bufferIndex; i++) {
+
// Serial.print(decoded[i]);
+
// Serial.print(", ");
+
// }
+
// Serial.println("DONE");
+
+
// Parse the decoded message
+
int i = 0;
+
+
uint8_t msgLength = decoded[i];
+
// Serial.print("MSG-LENGTH: ");
+
// Serial.println(msgLength);
+
+
uint8_t msgArr[msgLength];
+
i++;
+
while (i < 1 + msgLength) {
+
msgArr[i-1] = decoded[i];
+
i++;
+
}
+
+
// Serial.print("MSGARR: ");
+
// for (int i = 0; i < msgLength; i++) {
+
// Serial.print(msgArr[i]);
+
// Serial.print(", ");
+
// }
+
// Serial.println("MSGARR-END");
+
+
uint8_t payloadLength = decoded[i];
+
uint8_t payload[payloadLength];
+
i++;
+
while (i < 1 + msgLength + 1 + payloadLength) {
+
payload[i-1-msgLength-1] = decoded[i];
+
i++;
+
}
+
+
uint8_t msgCount = decoded[i];
+
+
// String msg = String((char*)msgArr);
+
String msg = byteArrayToString(msgArr, msgLength);
+
+
// Serial.println(msg);
+
+
// printArray("PAYLOAD", payload, payloadLength);
+
+
// Serial.print("MSGCOUNT: ");
+
// Serial.println(msgCount);
+
+
bool triggered = triggerEvent(msg, payload, payloadLength, msgCount);
+
+
bufferIndex = 0; // Reset the buffer for the next message
+
}
+
}
+
+
/* ------------------------------------------------------------ */
+
+
+
+
const int MAX_EVENTS = 255; // Maximum number of events to store, adjust as needed
+
EventCallback eventCallbacks[MAX_EVENTS];
+
int eventCount = 0;
+
+
const int REPLY_PAYLOAD_LENGTH = 255;
+
uint8_t reply[REPLY_PAYLOAD_LENGTH];
+
+
void on(String event, CallbackFunction callback) {
+
if (eventCount < MAX_EVENTS) {
+
eventCallbacks[eventCount].event = event;
+
eventCallbacks[eventCount].callback = callback;
+
eventCount = (eventCount + 1) % MAX_EVENTS;
+
} else {
+
// Serial.println("Max number of events reached. Wrapping events.");
+
}
+
}
+
+
bool triggerEvent(String event, uint8_t* payload, int payloadLength, uint8_t msgCount) {
+
for (int i = 0; i < eventCount; i++) {
+
if (eventCallbacks[i].event == event) {
+
// want to pass payload and payloadLength, need to get response payload
+
uint8_t reply_length = eventCallbacks[i].callback(payload, payloadLength, reply);
+
+
sendAck(msgCount, reply, reply_length);
+
return true;
+
}
+
}
+
+
// Serial.println(" No event registered.");
+
return false;
+
}
+
+
const int arrayLength = 7; // + length;
+
uint8_t byteArray[arrayLength];
+
+
void sendAck(uint8_t msgCount, uint8_t* reply, uint8_t length) {
+
// Serial.println("SEND ACK");
+
+
// int arrayLength = 7; // + length;
+
// static uint8_t byteArray[arrayLength];
+
+
byteArray[0] = 0x03;
+
byteArray[1] = 0x61;
+
byteArray[2] = 0x63;
+
byteArray[3] = 0x6B;
+
byteArray[4] = 0x00;
+
byteArray[5] = msgCount;
+
byteArray[6] = 0x0A;
+
+
// byteArray[4] = length;
+
// for (int i = 0; i < length; i++) {
+
// byteArray[i+5] = reply[i];
+
// }
+
// byteArray[5+length] = msgCount;
+
// byteArray[6+length] = 0x0A;
+
+
// Serial.println(msgCount);
+
// printArray("ACK", byteArray, 5+length);
+
+
Serial.write(byteArray, arrayLength);
+
+
// uint8_t byteArrayEncoded[arrayLength + 2]; // +2 for possible COBS overhead
+
// cobs_encode(byteArrayEncoded, byteArray, arrayLength);
+
// Serial.write(byteArrayEncoded, arrayLength + 2);
+
+
// printArray("ENCODED-ACK", byteArray, 5+length);
+
+
}
+
+
/* ------------------------------------------------------------ */
+
+
void cobs_encode(uint8_t *dst, const uint8_t *src, size_t len) {
+
size_t read_index = 0;
+
size_t write_index = 1;
+
size_t code_index = 0;
+
uint8_t code = 1;
+
+
while (read_index < len) {
+
if (src[read_index] == 0) {
+
dst[code_index] = code;
+
code = 1;
+
code_index = write_index++;
+
read_index++;
+
} else {
+
dst[write_index++] = src[read_index++];
+
code++;
+
if (code == 0xFF) {
+
dst[code_index] = code;
+
code = 1;
+
code_index = write_index++;
+
}
+
}
+
}
+
+
dst[code_index] = code;
+
+
// Add trailing zero
+
if (write_index < len + 2) {
+
dst[write_index] = 0;
+
}
+
}
+
+
void cobs_decode(uint8_t *dst, const uint8_t *src, size_t len) {
+
size_t i, j, dst_i = 0;
+
for (i = 0; i < len;) {
+
uint8_t code = src[i++];
+
for (j = 1; j < code && i < len; j++) {
+
dst[dst_i++] = src[i++];
+
}
+
if (code < 0xFF && dst_i < len) {
+
dst[dst_i++] = 0;
+
}
+
}
+
}
+
+
/* ------------------------------------------------------------ */
+
+
// TODO: THINK THIS IS BUGGY
+
void cobs_print(const String& message) {
+
// Convert the message to a byte array
+
int length = message.length();
+
uint8_t byteArray[length + 1]; // +1 for the null terminator
+
message.getBytes(byteArray, length + 1);
+
+
// Prepare the buffer for the encoded message
+
uint8_t encoded[length + 2]; // +2 for possible COBS overhead
+
+
// Perform COBS encoding
+
cobs_encode(encoded, byteArray, length + 1);
+
+
// Send the encoded message
+
Serial.write(encoded, length + 2); // Write the encoded bytes
+
}
+
+
/* ------------------------------------------------------------ */
+
+
float read_float(uint8_t* buffer, int index) {
+
uint8_t byte0 = buffer[index];
+
uint8_t byte1 = buffer[index+1];
+
uint8_t byte2 = buffer[index+2];
+
uint8_t byte3 = buffer[index+3];
+
+
uint8_t byteArray[] = {byte0, byte1, byte2, byte3};
+
float floatValue;
+
memcpy(&floatValue, &byteArray, sizeof(floatValue));
+
+
return floatValue;
+
}
+
+
int read_int(uint8_t* buffer, int index) {
+
uint8_t byte0 = buffer[index];
+
uint8_t byte1 = buffer[index+1];
+
uint8_t byte2 = buffer[index+2];
+
uint8_t byte3 = buffer[index+3];
+
+
uint8_t byteArray[] = {byte0, byte1, byte2, byte3};
+
int value;
+
memcpy(&value, &byteArray, sizeof(value));
+
+
return value;
+
}
+
+
String byteArrayToString(byte arr[], int length) {
+
String result = "";
+
+
for (int i = 0; i < length; i++) {
+
result += (char)arr[i]; // Convert each byte to a character and append it to the string
+
}
+
+
return result;
+
}
+
+
/* ------------------------------------------------------------------------ */
+
+
#define EPSILON 0.01
+
+
void goTo(float x, float y) {
+
+
// Serial.println("START GOTO");
+
+
// Set your target distances for each motor (in steps)
+
float motor1Target = (x + y) - pos[0];
+
float motor2Target = (y - x) - pos[1];
+
+
// Set motor direction based on target values
+
digitalWrite(motor1DirPin, motor1Target >= 0 ? HIGH : LOW);
+
digitalWrite(motor2DirPin, motor2Target >= 0 ? HIGH : LOW);
+
+
// Calculate the relative speeds and maximum duration for both motors
+
float maxSteps = max(abs(motor1Target), abs(motor2Target));
+
float motor1Speed = abs(motor1Target) / maxSteps;
+
float motor2Speed = abs(motor2Target) / maxSteps;
+
+
unsigned long stepDuration = 500; // The time it takes to perform one step in microseconds
+
unsigned long motor1StepInterval = stepDuration / motor1Speed;
+
unsigned long motor2StepInterval = stepDuration / motor2Speed;
+
+
// Initialize variables for step timing
+
unsigned long motor1PrevStepTime = 0;
+
unsigned long motor2PrevStepTime = 0;
+
float motor1Step = 0;
+
float motor2Step = 0;
+
+
// Loop until both motors reach their target steps
+
while (abs(motor1Target - motor1Step) > EPSILON || abs(motor2Target - motor2Step) > EPSILON) {
+
+
+
unsigned long currentTime = micros();
+
+
// Motor 1
+
if (abs(motor1Target - motor1Step) > EPSILON && ((currentTime - motor1PrevStepTime) >= motor1StepInterval)) {
+
digitalWrite(motor1StepPin, HIGH);
+
delayMicroseconds(1);
+
digitalWrite(motor1StepPin, LOW);
+
delayMicroseconds(1);
+
+
motor1Step += (motor1Target >= 0 ? 1.0 : -1.0)/SPU;
+
motor1PrevStepTime = currentTime;
+
}
+
+
// Motor 2
+
if (abs(motor2Target - motor2Step) > EPSILON && ((currentTime - motor2PrevStepTime) >= motor2StepInterval)) {
+
digitalWrite(motor2StepPin, HIGH);
+
delayMicroseconds(1);
+
digitalWrite(motor2StepPin, LOW);
+
delayMicroseconds(1);
+
+
motor2Step += (motor2Target >= 0 ? 1.0 : -1.0)/SPU;
+
motor2PrevStepTime = currentTime;
+
}
+
}
+
+
// Serial.println("END GOTO");
+
+
pos[0] += motor1Step;
+
pos[1] += motor2Step;
+
}
+
+
uint8_t go(uint8_t* payload, int length, uint8_t* reply) {
+
float x = read_float(payload, 0);
+
float y = read_float(payload, 4);
+
+
goTo(x, y);
+
+
return 0;
+
}
+
+
uint8_t moveServo(uint8_t* payload, int length, uint8_t* reply) {
+
int angle = read_int(payload, 0);
+
+
servo.writeMicroseconds(angle);
+
+
return 0;
+
}
+
+
/* ------ */
+
+
void printArray(String label, uint8_t* arr, int arrSize) {
+
Serial.print(label);
+
Serial.print("-BEGIN: ");
+
for (int i = 0; i < arrSize; i++) {
+
Serial.print(arr[i]);
+
Serial.print(", ");
+
}
+
Serial.print(label);
+
Serial.println("-END");
+
}