Intro
Sammy Semaphore is finally fully functional, complete with remote Bluetooth control. He turned out very well, although not without having to work through adversity. The biggest issues were supply chain and the fact that I got very sick.
Sammy Semaphore Signaling His Alphabet
Alphabet Firmware
//Sammy Semaphore //the semaphore alphabet is enacted at the same time as the display shows the corresponding character //Arduino UNO, 8x8 NEO pixel display, dual servo motors, Bluetooth //This demo displays the whole semaphore alphabet //by Doug Wong 2022 #include <FastLED.h> #define NLEDS 64 #define LEDpin 2 #include <Wire.h> #include <Adafruit_PWMServoDriver.h> CRGB leds[NLEDS]; Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); #define SERVOMIN 50 // This is the 'minimum' pulse length count (out of 4096) #define SERVOMAX 700 // This is the 'maximum' pulse length count (out of 4096) #define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates // Servo values for 6 arm positions uint16_t RS[6] = {83, 170, 269, 380, 455, 495}; uint16_t LS[6] = {480, 380, 294, 180, 106, 70}; //uint16_t RS[6] = {50, 175, 300, 475, 650, 700}; //uint16_t LS[6] = {700, 650, 475, 300, 175, 50}; uint16_t Rx = 50; // uint16_t Lx = 700; // uint16_t RC = 50; //right target uint16_t RT = 50; //right current uint16_t LC = 50; //left target uint16_t LT = 50; //left current // LED font for 8x8 pixels byte AL[28][8] = { {129, 129, 129, 255, 129, 129, 66, 60}, //A0 {127, 129, 129, 129, 127, 129, 129, 127}, //B1 {124, 130, 1, 1, 1, 1, 130, 124}, //C2 {63, 65, 129, 129, 129, 129, 65, 63}, //D3 {255, 1, 1, 1, 63, 1, 1, 255}, //E4 {1, 1, 1, 1, 63, 1, 1, 255}, //F5 {124, 130, 129, 225, 1, 1, 130, 124}, //G6 {129, 129, 129, 129, 255, 129, 129, 129}, //H7 {28, 8, 8, 8, 8, 8, 8, 28}, //I8 {30, 33, 33, 32, 32, 32, 32, 248}, //J9 {129, 65, 33, 17, 31, 33, 65, 129}, //K10 {255, 129, 1, 1, 1, 1, 1, 1}, //L11 {129, 129, 129, 129, 153, 165, 195, 129}, //M12 {129, 193, 161, 145, 145, 137, 133, 131}, //N13 {126, 129, 129, 129, 129, 129, 129, 126}, //O14 {1, 1, 1, 1, 127, 129, 129, 127}, //P15 {190, 65, 161, 129, 129, 129, 129, 126}, //Q16 {129, 65, 33, 127, 129, 129, 129, 127}, //R17 {126, 129, 128, 64, 62, 1, 129, 126}, //S18 {8, 8, 8, 8, 8, 8, 8, 127}, //T19 {126, 129, 129, 129, 129, 129, 129, 129}, //U20 {24, 36, 36, 66, 66, 129, 129, 129}, //V21 {129, 195, 165, 153, 129, 129, 129, 129}, //W22 {129, 66, 36, 24, 24, 36, 66, 129}, //X23 {16, 16, 16, 16, 24, 36, 66, 129}, //Y24 {255, 2, 4, 8, 16, 32, 64, 255}, //Z25 {255, 0, 0, 0, 0, 0, 0, 0}, //[space26 {36, 36, 255, 36, 36, 255, 36, 36} //\# }; //flag positions corresponding to each letter // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z SP # uint16_t rpos[28] = {0, 0, 0, 0, 3, 2, 1, 0, 5, 2, 4, 3, 2, 1, 5, 4, 3, 2, 1, 4, 3, 1, 2, 1, 2, 2, 0, 3}; uint16_t lpos[28] = {1, 2, 3, 4, 0, 0, 0, 2, 1, 4, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 5, 5, 3, 0, 0, 4}; char charac; char space = " "; char nn = "#"; char ABC[29] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ # "; // servo # setup uint8_t servo1 = 0; uint8_t servo2 = 1; void setup() { Serial.begin(9600); pwm.begin(); pwm.setOscillatorFrequency(27000000); pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates FastLED.addLeds<WS2812B, LEDpin, GRB>(leds, NLEDS); FastLED.setBrightness(50); } void loop() { for (int i = 0; i < 26; i++) { charac = ABC[i]; // Serial.println(charac); Mchar (charac, i); } charac = ABC[26]; // Serial.println(charac); Mchar (charac, 26); charac = ABC[27]; Mchar (charac, 27); } void Mchar(char Charac, int ic) { uint16_t rc, lc, tc; uint16_t ichar; ichar = int(Charac); //convert to integer if (ichar == 32) ichar = 91; //space if (ichar == 35) ichar = 92; //# ichar = ichar - 65; //set index to 0 tc = rpos[ichar]; rc = RS[tc]; //get right flag position PWM value tc = lpos[ichar]; lc = LS[tc]; //get left flag position PWM value pwm.setPWM(servo1, 0, rc); pwm.setPWM(servo2, 0, lc); BlankDisplay(); delay(100); DisplayChar(ichar); delay(1500); } void DisplayChar(int abc){ int rc, cc; for (rc = 0; rc < 8; rc++) { for (cc = 0; cc < 8; cc++) { if (bitRead(AL[abc][rc], cc) == 1) { leds[rc + cc * 8] = CRGB::Green; } else { leds[rc + cc * 8] = CRGB::Black; } } } FastLED.show(); } void BlankDisplay() { for (int ac = 0; ac < 64; ac++) { leds[ac] = CRGB::Black; } FastLED.show(); } void rmove(uint16_t rc, uint16_t rt) { if (rt > rc) for (uint16_t pulselen = RC; pulselen < RT; pulselen++) { pwm.setPWM(servo1, 0, pulselen); delay(5); } else for (uint16_t pulselen = RC; pulselen < RT; pulselen--) { pwm.setPWM(servo1, 0, pulselen); delay(5); } delay(1000); } void lmove(uint16_t lc, uint16_t lt) { if (lt > lc) for (uint16_t pulselen = LC; pulselen < LT; pulselen++) { pwm.setPWM(servo1, 0, pulselen); delay(2); } else for (uint16_t pulselen = LC; pulselen < LT; pulselen--) { pwm.setPWM(servo1, 0, pulselen); delay(2); } }
Sammy Semaphore Signaling Remotely Via Bluetooth
Bluetooth Semaphore Firmware
//Sammy Semaphore //semaphore signals can be controlled via Bluetooth - letters are displayed as both semaphores and corresponding characters //Arduino UNO, 8x8 NEO pixel display, dual servo motors, Bluetooth //by Doug Wong 2022 #include <FastLED.h> #define NLEDS 64 #define LEDpin 2 #include <Wire.h> #include <Adafruit_PWMServoDriver.h> CRGB leds[NLEDS]; Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); #define SERVOMIN 50 // This is the 'minimum' pulse length count (out of 4096) #define SERVOMAX 700 // This is the 'maximum' pulse length count (out of 4096) #define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates // Servo values for 6 arm positions uint16_t RS[6] = {83, 170, 269, 380, 455, 495}; uint16_t LS[6] = {480, 380, 294, 180, 106, 70}; //uint16_t RS[6] = {50, 175, 300, 475, 650, 700}; //uint16_t LS[6] = {700, 650, 475, 300, 175, 50}; uint16_t Rx = 50; // uint16_t Lx = 700; // uint16_t RC = 50; //right target uint16_t RT = 50; //right current uint16_t LC = 50; //left target uint16_t LT = 50; //left current // LED font for 8x8 pixels byte AL[28][8] = { {129, 129, 129, 255, 129, 129, 66, 60}, //A0 {127, 129, 129, 129, 127, 129, 129, 127}, //B1 {124, 130, 1, 1, 1, 1, 130, 124}, //C2 {63, 65, 129, 129, 129, 129, 65, 63}, //D3 {255, 1, 1, 1, 63, 1, 1, 255}, //E4 {1, 1, 1, 1, 63, 1, 1, 255}, //F5 {124, 130, 129, 225, 1, 1, 130, 124}, //G6 {129, 129, 129, 129, 255, 129, 129, 129}, //H7 {28, 8, 8, 8, 8, 8, 8, 28}, //I8 {30, 33, 33, 32, 32, 32, 32, 248}, //J9 {129, 65, 33, 17, 31, 33, 65, 129}, //K10 {255, 129, 1, 1, 1, 1, 1, 1}, //L11 {129, 129, 129, 129, 153, 165, 195, 129}, //M12 {129, 193, 161, 145, 145, 137, 133, 131}, //N13 {126, 129, 129, 129, 129, 129, 129, 126}, //O14 {1, 1, 1, 1, 127, 129, 129, 127}, //P15 {190, 65, 161, 129, 129, 129, 129, 126}, //Q16 {129, 65, 33, 127, 129, 129, 129, 127}, //R17 {126, 129, 128, 64, 62, 1, 129, 126}, //S18 {8, 8, 8, 8, 8, 8, 8, 127}, //T19 {126, 129, 129, 129, 129, 129, 129, 129}, //U20 {24, 36, 36, 66, 66, 129, 129, 129}, //V21 {129, 195, 165, 153, 129, 129, 129, 129}, //W22 {129, 66, 36, 24, 24, 36, 66, 129}, //X23 {16, 16, 16, 16, 24, 36, 66, 129}, //Y24 {255, 2, 4, 8, 16, 32, 64, 255}, //Z25 {255, 0, 0, 0, 0, 0, 0, 0}, //[space26 {36, 36, 255, 36, 36, 255, 36, 36} //\# }; //flag positions corresponding to each letter // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z SP # uint16_t rpos[28] = {0, 0, 0, 0, 3, 2, 1, 0, 5, 2, 4, 3, 2, 1, 5, 4, 3, 2, 1, 4, 3, 1, 2, 1, 2, 2, 0, 3}; uint16_t lpos[28] = {1, 2, 3, 4, 0, 0, 0, 2, 1, 4, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 5, 5, 3, 0, 0, 4}; char charac = "A"; char space = " "; char nn = "#"; char ABC[29] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ # "; char inputByte; uint16_t ichr; // servo # setup uint8_t servo1 = 0; uint8_t servo2 = 1; void setup() { Serial.begin(9600); pwm.begin(); pwm.setOscillatorFrequency(27000000); pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates FastLED.addLeds<WS2812B, LEDpin, GRB>(leds, NLEDS); FastLED.setBrightness(50); } void loop() { while(Serial.available()>0){ inputByte = Serial.read(); Serial.println(inputByte); ichr = int(inputByte); if (ichr == 32) ichr = 91; //space if (ichr == 35) ichr = 92; //# ichr = ichr - 65; //set index to 0 if (ichr < 0) ichr = 26; if (ichr < 28){ Serial.println(ichr); charac = ABC[ichr]; Mchar (charac); } } } void Mchar(char Charac) { uint16_t rc, lc, tc; uint16_t ichar; ichar = int(Charac); //convert to integer if (ichar == 32) ichar = 91; //space if (ichar == 35) ichar = 92; //# ichar = ichar - 65; //set index to 0 tc = rpos[ichar]; rc = RS[tc]; //get right flag position PWM value tc = lpos[ichar]; lc = LS[tc]; //get left flag position PWM value pwm.setPWM(servo1, 0, rc); pwm.setPWM(servo2, 0, lc); BlankDisplay(); delay(100); DisplayChar(ichar); delay(1500); } void DisplayChar(int abc){ int rc, cc; for (rc = 0; rc < 8; rc++) { for (cc = 0; cc < 8; cc++) { if (bitRead(AL[abc][rc], cc) == 1) { leds[rc + cc * 8] = CRGB::Green; } else { leds[rc + cc * 8] = CRGB::Black; } } } FastLED.show(); } void BlankDisplay() { for (int ac = 0; ac < 64; ac++) { leds[ac] = CRGB::Black; } FastLED.show(); } void rmove(uint16_t rc, uint16_t rt) { if (rt > rc) for (uint16_t pulselen = RC; pulselen < RT; pulselen++) { pwm.setPWM(servo1, 0, pulselen); delay(5); } else for (uint16_t pulselen = RC; pulselen < RT; pulselen--) { pwm.setPWM(servo1, 0, pulselen); delay(5); } delay(1000); } void lmove(uint16_t lc, uint16_t lt) { if (lt > lc) for (uint16_t pulselen = LC; pulselen < LT; pulselen++) { pwm.setPWM(servo1, 0, pulselen); delay(2); } else for (uint16_t pulselen = LC; pulselen < LT; pulselen--) { pwm.setPWM(servo1, 0, pulselen); delay(2); } }
Here is Sammy Semaphore Meets Nugget the Cat
Discussion For Blog 7
This project was a tester - with all kinds of supply chain issues and dealing with sickness. Overall I am satisfied with my effort, but not so much with the results.
Supply Chain Issues:
- I had many problems trying to acquire a complete set of TE Connectivity connectors.
- some orders were cancelled after they were accepted due to lack of stock
- my crimper could not do a great job on the pins in the kit
- The fact that my PCBs did not arrive, was quite depressing
- The servo motors that I ordered specially for this application were not the ones that arrived - so they had to be re-ordered and did not arrive in time
Sickness issues
Well I don't want to talk about getting Covid - it is just really painful to try and make progress when you are very sick.
What went well:
The Connector Tester design was interesting, including both the circuit design and the cycling mechanism. The PCBs are fairly simple so there is high confidence they will work. The design of Sammy Semaphore worked out well, it involved a lot of design and 3D printing, but I am very happy with the results, He manages to do a pretty good job even though he still does not have the correct motors.
What would I do differently?
I wouldn't get sick. Maybe I could expedite the PCB delivery. I might come up with a better way to tune servo positions. (it took a long time to sort out, partly because these servos work way better at 6V than 5 V.) I would learn C better (it is still a struggle that costs me time) I would blog more about the build steps, getting sick really curtailed what I could manage.
Conclusions
Some projects are going to un into adversity. We just need to fight through it and learn what we can from it. Although the connector tester part of the project did not go well, I did get to showcase building some really fabulous connectors. I am happy that at least the Sammy Semaphore part of the project was successful - it sometimes pays to have multiple irons in the fire. The project involved designing 2 systems, 2 PCBs, twelve 3-D printed parts, 5 firmware programs, 11 videos, several animations, and building half a dozen cables. Any project where I get to design some PCBs, design some mechanical mechanisms and make a system work, is a good project.
Thank you element14 and TE Connectivity for the opportunity to participate in this challenge. I learned a lot about semaphores and connectors.
Relevant Links:
Twist, Turn and Move Design Challenge with TE Robotics
Sammy Semaphore - blog 1
Connector Tester - blog 2
NEO Pixel Display - blog 3
Sammy Semaphore Torso Demo - Blog 4
I Am Sam I Am - Blog 5
TE Connectivity Connector Tester Blog 6
Sammy Semaphore Fully Functional Blog 7
Top Comments