a scrappy gimbal that insults you in shakespearean english
at main 12 kB view raw
1#include <Servo.h> 2 3#define SPU 80 // 20 teeth pulley, 16 microstep, 2mm a tooth belt, 16*200 microsteps per turn == (16*200)/(20*2) == 80 4#define PIN_SERVO D6 5 6Servo servo; 7 8typedef uint8_t (*CallbackFunction)(uint8_t*, int, uint8_t*); 9 10struct EventCallback { 11 String event; 12 CallbackFunction callback; 13}; 14 15 16float pos[] = {0, 0}; 17 18const int motor1StepPin = D10; 19const int motor1DirPin = D9; 20const int motor2StepPin = D8; 21const int motor2DirPin = D7; 22 23const int enablePin = D1; 24 25void setup() { 26 servo.attach(PIN_SERVO); 27 28 Serial.begin(9600); 29 30 // on("light", onLight); 31 on("go", go); 32 on("servo", moveServo); 33 on("motorsOn", motorsOn); 34 on("motorsOff", motorsOff); 35 on("moveTowardsOrigin", moveTowardsOrigin); 36 on("setOrigin", setOrigin); 37 38 pinMode(motor1StepPin, OUTPUT); 39 pinMode(motor1DirPin, OUTPUT); 40 pinMode(motor2StepPin, OUTPUT); 41 pinMode(motor2DirPin, OUTPUT); 42 43 pinMode(enablePin, OUTPUT); // enable pin 44 45 pinMode(PIN_LED, OUTPUT); 46 47 startupIndicator(); // once startup is finished, indicate to user 48} 49 50void loop() { 51 readSerial(); 52} 53 54void startupIndicator() { 55 // flash LED to indicate board is initialized 56 for (int i = 0; i < 3; i++) { 57 digitalWrite(PIN_LED, 0); 58 delay(200); 59 digitalWrite(PIN_LED, 1); 60 delay(200); 61 } 62} 63 64uint8_t onLight(uint8_t* payload, int length, uint8_t* reply) { 65 uint8_t value = payload[0]; 66 67 // Serial.println("light it up"); 68 69 digitalWrite(PIN_LED, value); 70 71 return 0; 72} 73 74uint8_t motorsOn(uint8_t* payload, int length, uint8_t* reply) { 75 digitalWrite(enablePin, 0); 76 return 0; 77} 78 79uint8_t motorsOff(uint8_t* payload, int length, uint8_t* reply) { 80 digitalWrite(enablePin, 1); 81 return 0; 82} 83 84uint8_t moveTowardsOrigin(uint8_t* payload, int length, uint8_t* reply) { 85 float x = pos[0]; 86 float y = pos[1]; 87 88 // this ternary may not be neccesary 89 goTo( 90 x + ( x < 0 ? 10 : -10), 91 y + ( y < 0 ? 10 : -10) 92 ); 93 94 return 0; 95} 96 97uint8_t setOrigin(uint8_t* payload, int length, uint8_t* reply) { 98 pos[0] = 0; 99 pos[1] = 0; 100 101 return 0; 102} 103 104 105/* ------------------------------------------------------------ */ 106 107int bufferIndex = 0; 108uint8_t msgBuffer[100]; 109 110void readSerial() { 111 while (Serial.available() > 0) { 112 uint8_t incoming = Serial.read(); 113 114 msgBuffer[bufferIndex] = incoming; 115 116 if (incoming != 0) { 117 bufferIndex++; 118 continue; // Proceed to the next byte if current byte is not null 119 } 120 121 // Serial.print("RECEIVED: "); 122 // for (int i = 0; i < bufferIndex; i++) { 123 // Serial.print(msgBuffer[i]); 124 // Serial.print(", "); 125 // } 126 // Serial.println("DONE"); 127 128 // Now we have a full message, perform COBS decoding 129 uint8_t decoded[bufferIndex]; 130 cobs_decode(decoded, msgBuffer, bufferIndex); 131 132 // Serial.print("DECODED: "); 133 // for (int i = 0; i < bufferIndex; i++) { 134 // Serial.print(decoded[i]); 135 // Serial.print(", "); 136 // } 137 // Serial.println("DONE"); 138 139 // Parse the decoded message 140 int i = 0; 141 142 uint8_t msgLength = decoded[i]; 143 // Serial.print("MSG-LENGTH: "); 144 // Serial.println(msgLength); 145 146 uint8_t msgArr[msgLength]; 147 i++; 148 while (i < 1 + msgLength) { 149 msgArr[i-1] = decoded[i]; 150 i++; 151 } 152 153 // Serial.print("MSGARR: "); 154 // for (int i = 0; i < msgLength; i++) { 155 // Serial.print(msgArr[i]); 156 // Serial.print(", "); 157 // } 158 // Serial.println("MSGARR-END"); 159 160 uint8_t payloadLength = decoded[i]; 161 uint8_t payload[payloadLength]; 162 i++; 163 while (i < 1 + msgLength + 1 + payloadLength) { 164 payload[i-1-msgLength-1] = decoded[i]; 165 i++; 166 } 167 168 uint8_t msgCount = decoded[i]; 169 170 // String msg = String((char*)msgArr); 171 String msg = byteArrayToString(msgArr, msgLength); 172 173 // Serial.println(msg); 174 175 // printArray("PAYLOAD", payload, payloadLength); 176 177 // Serial.print("MSGCOUNT: "); 178 // Serial.println(msgCount); 179 180 bool triggered = triggerEvent(msg, payload, payloadLength, msgCount); 181 182 bufferIndex = 0; // Reset the buffer for the next message 183 } 184} 185 186/* ------------------------------------------------------------ */ 187 188 189 190const int MAX_EVENTS = 255; // Maximum number of events to store, adjust as needed 191EventCallback eventCallbacks[MAX_EVENTS]; 192int eventCount = 0; 193 194const int REPLY_PAYLOAD_LENGTH = 255; 195uint8_t reply[REPLY_PAYLOAD_LENGTH]; 196 197void on(String event, CallbackFunction callback) { 198 if (eventCount < MAX_EVENTS) { 199 eventCallbacks[eventCount].event = event; 200 eventCallbacks[eventCount].callback = callback; 201 eventCount = (eventCount + 1) % MAX_EVENTS; 202 } else { 203 // Serial.println("Max number of events reached. Wrapping events."); 204 } 205} 206 207bool triggerEvent(String event, uint8_t* payload, int payloadLength, uint8_t msgCount) { 208 for (int i = 0; i < eventCount; i++) { 209 if (eventCallbacks[i].event == event) { 210 // want to pass payload and payloadLength, need to get response payload 211 uint8_t reply_length = eventCallbacks[i].callback(payload, payloadLength, reply); 212 213 sendAck(msgCount, reply, reply_length); 214 return true; 215 } 216 } 217 218 // Serial.println(" No event registered."); 219 return false; 220} 221 222const int arrayLength = 7; // + length; 223uint8_t byteArray[arrayLength]; 224 225void sendAck(uint8_t msgCount, uint8_t* reply, uint8_t length) { 226 // Serial.println("SEND ACK"); 227 228 // int arrayLength = 7; // + length; 229 // static uint8_t byteArray[arrayLength]; 230 231 byteArray[0] = 0x03; 232 byteArray[1] = 0x61; 233 byteArray[2] = 0x63; 234 byteArray[3] = 0x6B; 235 byteArray[4] = 0x00; 236 byteArray[5] = msgCount; 237 byteArray[6] = 0x0A; 238 239 // byteArray[4] = length; 240 // for (int i = 0; i < length; i++) { 241 // byteArray[i+5] = reply[i]; 242 // } 243 // byteArray[5+length] = msgCount; 244 // byteArray[6+length] = 0x0A; 245 246 // Serial.println(msgCount); 247 // printArray("ACK", byteArray, 5+length); 248 249 Serial.write(byteArray, arrayLength); 250 251 // uint8_t byteArrayEncoded[arrayLength + 2]; // +2 for possible COBS overhead 252 // cobs_encode(byteArrayEncoded, byteArray, arrayLength); 253 // Serial.write(byteArrayEncoded, arrayLength + 2); 254 255 // printArray("ENCODED-ACK", byteArray, 5+length); 256 257} 258 259/* ------------------------------------------------------------ */ 260 261void cobs_encode(uint8_t *dst, const uint8_t *src, size_t len) { 262 size_t read_index = 0; 263 size_t write_index = 1; 264 size_t code_index = 0; 265 uint8_t code = 1; 266 267 while (read_index < len) { 268 if (src[read_index] == 0) { 269 dst[code_index] = code; 270 code = 1; 271 code_index = write_index++; 272 read_index++; 273 } else { 274 dst[write_index++] = src[read_index++]; 275 code++; 276 if (code == 0xFF) { 277 dst[code_index] = code; 278 code = 1; 279 code_index = write_index++; 280 } 281 } 282 } 283 284 dst[code_index] = code; 285 286 // Add trailing zero 287 if (write_index < len + 2) { 288 dst[write_index] = 0; 289 } 290} 291 292void cobs_decode(uint8_t *dst, const uint8_t *src, size_t len) { 293 size_t i, j, dst_i = 0; 294 for (i = 0; i < len;) { 295 uint8_t code = src[i++]; 296 for (j = 1; j < code && i < len; j++) { 297 dst[dst_i++] = src[i++]; 298 } 299 if (code < 0xFF && dst_i < len) { 300 dst[dst_i++] = 0; 301 } 302 } 303} 304 305/* ------------------------------------------------------------ */ 306 307// TODO: THINK THIS IS BUGGY 308void cobs_print(const String& message) { 309 // Convert the message to a byte array 310 int length = message.length(); 311 uint8_t byteArray[length + 1]; // +1 for the null terminator 312 message.getBytes(byteArray, length + 1); 313 314 // Prepare the buffer for the encoded message 315 uint8_t encoded[length + 2]; // +2 for possible COBS overhead 316 317 // Perform COBS encoding 318 cobs_encode(encoded, byteArray, length + 1); 319 320 // Send the encoded message 321 Serial.write(encoded, length + 2); // Write the encoded bytes 322} 323 324/* ------------------------------------------------------------ */ 325 326float read_float(uint8_t* buffer, int index) { 327 uint8_t byte0 = buffer[index]; 328 uint8_t byte1 = buffer[index+1]; 329 uint8_t byte2 = buffer[index+2]; 330 uint8_t byte3 = buffer[index+3]; 331 332 uint8_t byteArray[] = {byte0, byte1, byte2, byte3}; 333 float floatValue; 334 memcpy(&floatValue, &byteArray, sizeof(floatValue)); 335 336 return floatValue; 337} 338 339int read_int(uint8_t* buffer, int index) { 340 uint8_t byte0 = buffer[index]; 341 uint8_t byte1 = buffer[index+1]; 342 uint8_t byte2 = buffer[index+2]; 343 uint8_t byte3 = buffer[index+3]; 344 345 uint8_t byteArray[] = {byte0, byte1, byte2, byte3}; 346 int value; 347 memcpy(&value, &byteArray, sizeof(value)); 348 349 return value; 350} 351 352String byteArrayToString(byte arr[], int length) { 353 String result = ""; 354 355 for (int i = 0; i < length; i++) { 356 result += (char)arr[i]; // Convert each byte to a character and append it to the string 357 } 358 359 return result; 360} 361 362/* ------------------------------------------------------------------------ */ 363 364#define EPSILON 0.01 365 366void goTo(float x, float y) { 367 368 // Serial.println("START GOTO"); 369 370 // Set your target distances for each motor (in steps) 371 float motor1Target = (x + y) - pos[0]; 372 float motor2Target = (y - x) - pos[1]; 373 374 // Set motor direction based on target values 375 digitalWrite(motor1DirPin, motor1Target >= 0 ? HIGH : LOW); 376 digitalWrite(motor2DirPin, motor2Target >= 0 ? HIGH : LOW); 377 378 // Calculate the relative speeds and maximum duration for both motors 379 float maxSteps = max(abs(motor1Target), abs(motor2Target)); 380 float motor1Speed = abs(motor1Target) / maxSteps; 381 float motor2Speed = abs(motor2Target) / maxSteps; 382 383 unsigned long stepDuration = 500; // The time it takes to perform one step in microseconds 384 unsigned long motor1StepInterval = stepDuration / motor1Speed; 385 unsigned long motor2StepInterval = stepDuration / motor2Speed; 386 387 // Initialize variables for step timing 388 unsigned long motor1PrevStepTime = 0; 389 unsigned long motor2PrevStepTime = 0; 390 float motor1Step = 0; 391 float motor2Step = 0; 392 393 // Loop until both motors reach their target steps 394 while (abs(motor1Target - motor1Step) > EPSILON || abs(motor2Target - motor2Step) > EPSILON) { 395 396 397 unsigned long currentTime = micros(); 398 399 // Motor 1 400 if (abs(motor1Target - motor1Step) > EPSILON && ((currentTime - motor1PrevStepTime) >= motor1StepInterval)) { 401 digitalWrite(motor1StepPin, HIGH); 402 delayMicroseconds(1); 403 digitalWrite(motor1StepPin, LOW); 404 delayMicroseconds(1); 405 406 motor1Step += (motor1Target >= 0 ? 1.0 : -1.0)/SPU; 407 motor1PrevStepTime = currentTime; 408 } 409 410 // Motor 2 411 if (abs(motor2Target - motor2Step) > EPSILON && ((currentTime - motor2PrevStepTime) >= motor2StepInterval)) { 412 digitalWrite(motor2StepPin, HIGH); 413 delayMicroseconds(1); 414 digitalWrite(motor2StepPin, LOW); 415 delayMicroseconds(1); 416 417 motor2Step += (motor2Target >= 0 ? 1.0 : -1.0)/SPU; 418 motor2PrevStepTime = currentTime; 419 } 420 } 421 422 // Serial.println("END GOTO"); 423 424 pos[0] += motor1Step; 425 pos[1] += motor2Step; 426} 427 428uint8_t go(uint8_t* payload, int length, uint8_t* reply) { 429 float x = read_float(payload, 0); 430 float y = read_float(payload, 4); 431 432 goTo(x, y); 433 434 return 0; 435} 436 437uint8_t moveServo(uint8_t* payload, int length, uint8_t* reply) { 438 int angle = read_int(payload, 0); 439 440 servo.writeMicroseconds(angle); 441 442 return 0; 443} 444 445/* ------ */ 446 447void printArray(String label, uint8_t* arr, int arrSize) { 448 Serial.print(label); 449 Serial.print("-BEGIN: "); 450 for (int i = 0; i < arrSize; i++) { 451 Serial.print(arr[i]); 452 Serial.print(", "); 453 } 454 Serial.print(label); 455 Serial.println("-END"); 456}