Debug: Database connection successful Robotics Education Root Topic (Page 3) / Science, Technology, and Astronomy / New Mars Forums

Announcement

Announcement: This forum is accepting new registrations via email. Please see Recruiting Topic for additional information. Write newmarsmember[at_symbol]gmail.com.

#51 2026-08-25 15:17:02

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post will hold V69 of Cokoino sketch ...

This version includes a new blinking red LED alert.

In working on the planning for a coordinated robot arm movement, we realized the arm may be in an unsuitable position when the request for forward movement is issued.  In that event, we are planning to run calculations to see if the requested movement is feasible given the starting coordinates of the arms. If the movement is not feasible, our current thinking is to stop the run and set the Cokoino board to blinking red. We will then require the operator to reset the Cokoino with the start button, and re-position the robot arm.

// CokoinoV69.ino Prepared by Gemini Supervised by Tom Hanson
// Version 69: Added non-blocking ERROR:OUT_OF_BOUNDS blinking red LED state.
// Version 68: Updated START token version string to V68.
// Version 62: No change except version number Note Ready: version
// Version 59: Updated version strings for V59 integration with Gated Telemetry handshake.
// Version 58: Introduced Gated Telemetry to completely eliminate analog line chatter.

#include <PS2X_lib.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN A1
#define LED_COUNT 4
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

PS2X ps2x;
int error = 0;
bool runtimeActive = false;

// --- GATED TELEMETRY STATE ---
bool analogEnabled = false; // Muted by default to keep the transmission pipe silent

// --- ERROR / ALARM STATE ---
bool isBlinkingError = false;
unsigned long lastBlinkTime = 0;
bool errorLedState = false;
const unsigned long BLINK_INTERVAL = 250; // 250ms toggle rate (4Hz blink)

void setLEDs(uint32_t c) {
  for(int i=0; i<LED_COUNT; i++) strip.setPixelColor(i, c);
  strip.show();
}

void transmitToken(String token) {
  // Clearing error state on explicit user action/reset tokens
  if (token == "START Sketch Version V69" || token == "TRIANGLE" || token == "CIRCLE") {
    isBlinkingError = false;
  }

  if (!runtimeActive) {
    setLEDs(strip.Color(255, 150, 0)); // Yellow signal on key test
  }
  
  Serial.println(token);
  
  if (!runtimeActive) {
    delay(60); 
    setLEDs(strip.Color(0, 255, 0));   // Green = Standby
  }
}

void setup(){
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(40);
  strip.show(); 

  error = ps2x.config_gamepad(10, 12, 11, 13);
  if(error == 0) Serial.println("V69 Ready: Pass-Through Mode Enabled.");
}

void loop(){
  if(error != 0) return;
  
  ps2x.read_gamepad(false, 0);

  // ZONE 1: ANALOG STREAMING ENGINE (GATED PASS-THROUGH)
  if (analogEnabled) {
    int lx = ps2x.Analog(PSS_LX);
    int ly = ps2x.Analog(PSS_LY);
    int rx = ps2x.Analog(PSS_RX);
    int ry = ps2x.Analog(PSS_RY);
    
    // Smooth 0-255 pass-through active ONLY when steering mode is live
    if (abs(lx - 128) > 15) { Serial.print("ANALOG:LX:"); Serial.println(lx); }
    if (abs(ly - 128) > 15) { Serial.print("ANALOG:LY:"); Serial.println(ly); }
    if (abs(rx - 128) > 15) { Serial.print("ANALOG:RX:"); Serial.println(rx); }
    if (abs(ry - 128) > 15) { Serial.print("ANALOG:RY:"); Serial.println(ry); }
  }

  // ZONE 2: DIGITAL TRANSMISSION PIPE
  if(ps2x.ButtonPressed(PSB_TRIANGLE))  transmitToken("TRIANGLE");
  if(ps2x.ButtonReleased(PSB_TRIANGLE)) transmitToken("TRIANGLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CIRCLE))    transmitToken("CIRCLE");
  if(ps2x.ButtonReleased(PSB_CIRCLE))   transmitToken("CIRCLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CROSS))     transmitToken("CROSS");
  if(ps2x.ButtonReleased(PSB_CROSS))    transmitToken("CROSS RELEASED");
  if(ps2x.ButtonPressed(PSB_SQUARE))    transmitToken("SQUARE");
  if(ps2x.ButtonReleased(PSB_SQUARE))   transmitToken("SQUARE RELEASED");

  if(ps2x.ButtonPressed(PSB_START)) {
    runtimeActive = true; 
    transmitToken("START Sketch Version V69");
  }
  if(ps2x.ButtonPressed(PSB_SELECT))    transmitToken("SELECT");
  if(ps2x.ButtonReleased(PSB_SELECT))   transmitToken("SELECT RELEASED");

  if(ps2x.ButtonPressed(PSB_PAD_UP))     transmitToken("PAD UP");
  if(ps2x.ButtonReleased(PSB_PAD_UP))    transmitToken("PAD UP RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_DOWN))   transmitToken("PAD DOWN");
  if(ps2x.ButtonReleased(PSB_PAD_DOWN))  transmitToken("PAD DOWN RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_LEFT))   transmitToken("PAD LEFT");
  if(ps2x.ButtonReleased(PSB_PAD_LEFT))  transmitToken("PAD LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_RIGHT))  transmitToken("PAD RIGHT");
  if(ps2x.ButtonReleased(PSB_PAD_RIGHT)) transmitToken("PAD RIGHT RELEASED");

  if(ps2x.ButtonPressed(PSB_L1))        transmitToken("TOOL ADVANCE");
  if(ps2x.ButtonReleased(PSB_L1))       transmitToken("TOOL ADVANCE RELEASED");
  if(ps2x.ButtonPressed(PSB_L2))        transmitToken("TOOL RETRACT");
  if(ps2x.ButtonReleased(PSB_L2))       transmitToken("TOOL RETRACT RELEASED");
  if(ps2x.ButtonPressed(PSB_R1))        transmitToken("READY POSITION");
  if(ps2x.ButtonReleased(PSB_R1))       transmitToken("READY POSITION RELEASED");
  if(ps2x.ButtonPressed(PSB_R2))        transmitToken("SYSTEM HOME");
  if(ps2x.ButtonReleased(PSB_R2))       transmitToken("SYSTEM HOME RELEASED");

  if(ps2x.ButtonPressed(PSB_L3))        transmitToken("STICK CLICK LEFT");
  if(ps2x.ButtonReleased(PSB_L3))       transmitToken("STICK CLICK LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_R3))        transmitToken("STICK CLICK RIGHT");
  if(ps2x.ButtonReleased(PSB_R3))       transmitToken("STICK CLICK RIGHT RELEASED");

  // ZONE 3: INCOMING SERIAL LISTENER (LIGHTING & TELEMETRY CONTROL)
  if (Serial.available() > 0) {
    String rcved = Serial.readStringUntil('\n');
    rcved.trim();
    
    // Telemetry Gate Control Commands
    if (rcved == "ANALOG:ENABLE") {
      analogEnabled = true;
    }
    else if (rcved == "ANALOG:DISABLE") {
      analogEnabled = false;
    }
    // Error / Alarm Signal
    else if (rcved == "ERROR:OUT_OF_BOUNDS") {
      isBlinkingError = true;
      lastBlinkTime = millis();
      errorLedState = true;
      setLEDs(strip.Color(255, 0, 0));
    }
    // Existing LED commands
    else if (rcved.startsWith("LED:LOCK:")) {
      isBlinkingError = false;
      char hexChar = rcved.charAt(9);
      int count = (hexChar >= 'A') ? (hexChar - 'A' + 10) : (hexChar - '0');
      
      uint32_t activeColor   = strip.Color(0, 0, 255);
      uint32_t inactiveColor = strip.Color(10, 10, 10);

      strip.setPixelColor(3, (count & 1) ? activeColor : inactiveColor);
      strip.setPixelColor(2, (count & 2) ? activeColor : inactiveColor);
      strip.setPixelColor(1, (count & 4) ? activeColor : inactiveColor);
      strip.setPixelColor(0, (count & 8) ? activeColor : inactiveColor);
      strip.show();
    }
    else if (rcved == "LED:STATE:GREEN") {
      isBlinkingError = false;
      setLEDs(strip.Color(0, 255, 0)); 
    }
  }

  // ZONE 4: NON-BLOCKING BLINK ENGINE
  if (isBlinkingError) {
    unsigned long currentMillis = millis();
    if (currentMillis - lastBlinkTime >= BLINK_INTERVAL) {
      lastBlinkTime = currentMillis;
      errorLedState = !errorLedState;
      if (errorLedState) {
        setLEDs(strip.Color(255, 0, 0));
      } else {
        setLEDs(strip.Color(0, 0, 0));
      }
    }
  }
  
  delay(40);
}

(th)

Offline

Like button can go here

#52 2026-08-30 16:01:36

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post will hold V72 of Cokoino sketch for interface to LynxMotion arm.

This version of the sketch should be identical to V69 except for version change, but I won't know that until I perform difference processing.

// CokoinoV72.ino Prepared by Gemini Supervised by Tom Hanson
// Version 72: Version string updated to match bridge V72. Integrated non-blocking red LED alarm.
// Version 69: Added non-blocking ERROR:OUT_OF_BOUNDS blinking red LED state.

#include <PS2X_lib.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN A1
#define LED_COUNT 4
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

PS2X ps2x;
int error = 0;
bool runtimeActive = false;

// --- GATED TELEMETRY STATE ---
bool analogEnabled = false; // Muted by default to keep the transmission pipe silent

// --- ERROR / ALARM STATE ---
bool isBlinkingError = false;
unsigned long lastBlinkTime = 0;
bool errorLedState = false;
const unsigned long BLINK_INTERVAL = 250; // 250ms toggle rate (4Hz blink)

void setLEDs(uint32_t c) {
  for(int i=0; i<LED_COUNT; i++) strip.setPixelColor(i, c);
  strip.show();
}

void transmitToken(String token) {
  // Clearing error state on explicit user action/reset tokens
  if (token == "START Sketch Version V72" || token == "TRIANGLE" || token == "CIRCLE") {
    isBlinkingError = false;
  }

  if (!runtimeActive) {
    setLEDs(strip.Color(255, 150, 0)); // Yellow signal on key test
  }
  
  Serial.println(token);
  
  if (!runtimeActive) {
    delay(60); 
    setLEDs(strip.Color(0, 255, 0));   // Green = Standby
  }
}

void setup(){
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(40);
  strip.show(); 

  error = ps2x.config_gamepad(10, 12, 11, 13);
  if(error == 0) Serial.println("V72 Ready: Pass-Through Mode Enabled.");
}

void loop(){
  if(error != 0) return;
  
  ps2x.read_gamepad(false, 0);

  // ZONE 1: ANALOG STREAMING ENGINE (GATED PASS-THROUGH)
  if (analogEnabled) {
    int lx = ps2x.Analog(PSS_LX);
    int ly = ps2x.Analog(PSS_LY);
    int rx = ps2x.Analog(PSS_RX);
    int ry = ps2x.Analog(PSS_RY);
    
    if (abs(lx - 128) > 15) { Serial.print("ANALOG:LX:"); Serial.println(lx); }
    if (abs(ly - 128) > 15) { Serial.print("ANALOG:LY:"); Serial.println(ly); }
    if (abs(rx - 128) > 15) { Serial.print("ANALOG:RX:"); Serial.println(rx); }
    if (abs(ry - 128) > 15) { Serial.print("ANALOG:RY:"); Serial.println(ry); }
  }

  // ZONE 2: DIGITAL TRANSMISSION PIPE
  if(ps2x.ButtonPressed(PSB_TRIANGLE))  transmitToken("TRIANGLE");
  if(ps2x.ButtonReleased(PSB_TRIANGLE)) transmitToken("TRIANGLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CIRCLE))    transmitToken("CIRCLE");
  if(ps2x.ButtonReleased(PSB_CIRCLE))   transmitToken("CIRCLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CROSS))     transmitToken("CROSS");
  if(ps2x.ButtonReleased(PSB_CROSS))    transmitToken("CROSS RELEASED");
  if(ps2x.ButtonPressed(PSB_SQUARE))    transmitToken("SQUARE");
  if(ps2x.ButtonReleased(PSB_SQUARE))   transmitToken("SQUARE RELEASED");

  if(ps2x.ButtonPressed(PSB_START)) {
    runtimeActive = true; 
    transmitToken("START Sketch Version V72");
  }
  if(ps2x.ButtonPressed(PSB_SELECT))    transmitToken("SELECT");
  if(ps2x.ButtonReleased(PSB_SELECT))   transmitToken("SELECT RELEASED");

  if(ps2x.ButtonPressed(PSB_PAD_UP))     transmitToken("PAD UP");
  if(ps2x.ButtonReleased(PSB_PAD_UP))    transmitToken("PAD UP RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_DOWN))   transmitToken("PAD DOWN");
  if(ps2x.ButtonReleased(PSB_PAD_DOWN))  transmitToken("PAD DOWN RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_LEFT))   transmitToken("PAD LEFT");
  if(ps2x.ButtonReleased(PSB_PAD_LEFT))  transmitToken("PAD LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_RIGHT))  transmitToken("PAD RIGHT");
  if(ps2x.ButtonReleased(PSB_PAD_RIGHT)) transmitToken("PAD RIGHT RELEASED");

  if(ps2x.ButtonPressed(PSB_L1))        transmitToken("TOOL ADVANCE");
  if(ps2x.ButtonReleased(PSB_L1))       transmitToken("TOOL ADVANCE RELEASED");
  if(ps2x.ButtonPressed(PSB_L2))        transmitToken("TOOL RETRACT");
  if(ps2x.ButtonReleased(PSB_L2))       transmitToken("TOOL RETRACT RELEASED");
  if(ps2x.ButtonPressed(PSB_R1))        transmitToken("READY POSITION");
  if(ps2x.ButtonReleased(PSB_R1))       transmitToken("READY POSITION RELEASED");
  if(ps2x.ButtonPressed(PSB_R2))        transmitToken("SYSTEM HOME");
  if(ps2x.ButtonReleased(PSB_R2))       transmitToken("SYSTEM HOME RELEASED");

  if(ps2x.ButtonPressed(PSB_L3))        transmitToken("STICK CLICK LEFT");
  if(ps2x.ButtonReleased(PSB_L3))       transmitToken("STICK CLICK LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_R3))        transmitToken("STICK CLICK RIGHT");
  if(ps2x.ButtonReleased(PSB_R3))       transmitToken("STICK CLICK RIGHT RELEASED");

  // ZONE 3: INCOMING SERIAL LISTENER
  if (Serial.available() > 0) {
    String rcved = Serial.readStringUntil('\n');
    rcved.trim();
    
    if (rcved == "ANALOG:ENABLE") {
      analogEnabled = true;
    }
    else if (rcved == "ANALOG:DISABLE") {
      analogEnabled = false;
    }
    else if (rcved == "ERROR:OUT_OF_BOUNDS") {
      isBlinkingError = true;
      lastBlinkTime = millis();
      errorLedState = true;
      setLEDs(strip.Color(255, 0, 0));
    }
    else if (rcved.startsWith("LED:LOCK:")) {
      isBlinkingError = false;
      char hexChar = rcved.charAt(9);
      int count = (hexChar >= 'A') ? (hexChar - 'A' + 10) : (hexChar - '0');
      
      uint32_t activeColor   = strip.Color(0, 0, 255);
      uint32_t inactiveColor = strip.Color(10, 10, 10);

      strip.setPixelColor(3, (count & 1) ? activeColor : inactiveColor);
      strip.setPixelColor(2, (count & 2) ? activeColor : inactiveColor);
      strip.setPixelColor(1, (count & 4) ? activeColor : inactiveColor);
      strip.setPixelColor(0, (count & 8) ? activeColor : inactiveColor);
      strip.show();
    }
    else if (rcved == "LED:STATE:GREEN") {
      isBlinkingError = false;
      setLEDs(strip.Color(0, 255, 0)); 
    }
  }

  // ZONE 4: NON-BLOCKING BLINK ENGINE
  if (isBlinkingError) {
    unsigned long currentMillis = millis();
    if (currentMillis - lastBlinkTime >= BLINK_INTERVAL) {
      lastBlinkTime = currentMillis;
      errorLedState = !errorLedState;
      setLEDs(errorLedState ? strip.Color(255, 0, 0) : strip.Color(0, 0, 0));
    }
  }
  
  delay(40);
}

This version (V69 on) is supposed to blink red LED's if the Raspberry Pi detects an impossible request. An impossible request can occur if the operator places the arm in a position from which a subsequent movement cannot occur in the Real Universe. An example is a request to advance the gripper 5 centimeters in X, if the arm is positioned straight up.  The joint can certainly move in X, but it cannot maintain Z, because the arm moves in a curvel 

(th)
(th)

Offline

Like button can go here

#53 2026-09-13 13:12:29

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

Work on a system to allow a Cokoino gamepad controller to operate a LynxMotion robotic arm has been reported here and in one or more of the robotics topics, and extensively in the Python topic.

ChatGPT and I have just put finishing touches on V81 of the Python program.  We have tested the robot arm move calculations extensively in mathematics exercises which were reported to the log file.  Earlier we attempted a 1 centimeter move and ran into difficulty because the LynxMotion onboard controller regarded the small moves as noise.  After considerable more math testing, we've arrived at the point of sending a full five centimeter move command.  Our calculations appear to show that the move request is large enough so the onboard SSC32u controller will honor the request, and then perform it's own move calculations to plan the move of the entire arm to the requested destination in the time specified, which is currently 3 seconds.

My original goal of trying to perform the maneuver with zero Z movement turns out to be mathematically possible but not practical. However, the amount of Z motion is likely to be on the order of 1 millimeter so most humans won't see the movement. 

(th)

Offline

Like button can go here

#54 2026-09-19 14:53:27

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post is for Version 84 oif the Cokoino sketch that works with Python Version 84 to control a LynxMotion AL5D robot arm.

// CokoinoV84.ino Prepared by Gemini Supervised by Tom Hanson
// ChatGPT will handle updates from here on. 
// Version 69: Added non-blocking ERROR:OUT_OF_BOUNDS blinking red LED state.
// Version 68: Updated START token version string to V68.
// Version 62: No change except version number Note Ready: version
// Version 59: Updated version strings for V59 integration with Gated Telemetry handshake.
// Version 58: Introduced Gated Telemetry to completely eliminate analog line chatter.

#include <PS2X_lib.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN A1
#define LED_COUNT 4
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

PS2X ps2x;
int error = 0;
bool runtimeActive = false;

// --- GATED TELEMETRY STATE ---
bool analogEnabled = false; // Muted by default to keep the transmission pipe silent

// --- ERROR / ALARM STATE ---
bool isBlinkingError = false;
unsigned long lastBlinkTime = 0;
bool errorLedState = false;
const unsigned long BLINK_INTERVAL = 250; // 250ms toggle rate (4Hz blink)

void setLEDs(uint32_t c) {
  for(int i=0; i<LED_COUNT; i++) strip.setPixelColor(i, c);
  strip.show();
}

void transmitToken(String token) {
  // Clearing error state on explicit user action/reset tokens
  if (token == "START Sketch Version V84" || token == "TRIANGLE" || token == "CIRCLE") {
    isBlinkingError = false;
  }

  if (!runtimeActive) {
    setLEDs(strip.Color(255, 150, 0)); // Yellow signal on key test
  }
  
  Serial.println(token);
  
  if (!runtimeActive) {
    delay(60); 
    setLEDs(strip.Color(0, 255, 0));   // Green = Standby
  }
}

void setup(){
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(40);
  strip.show(); 

  error = ps2x.config_gamepad(10, 12, 11, 13);
  if(error == 0) Serial.println("V84 Ready: Pass-Through Mode Enabled.");
}

void loop(){
  if(error != 0) return;
  
  ps2x.read_gamepad(false, 0);

  // ZONE 1: ANALOG STREAMING ENGINE (GATED PASS-THROUGH)
  if (analogEnabled) {
    int lx = ps2x.Analog(PSS_LX);
    int ly = ps2x.Analog(PSS_LY);
    int rx = ps2x.Analog(PSS_RX);
    int ry = ps2x.Analog(PSS_RY);
    
    // Smooth 0-255 pass-through active ONLY when steering mode is live
    if (abs(lx - 128) > 15) { Serial.print("ANALOG:LX:"); Serial.println(lx); }
    if (abs(ly - 128) > 15) { Serial.print("ANALOG:LY:"); Serial.println(ly); }
    if (abs(rx - 128) > 15) { Serial.print("ANALOG:RX:"); Serial.println(rx); }
    if (abs(ry - 128) > 15) { Serial.print("ANALOG:RY:"); Serial.println(ry); }
  }

  // ZONE 2: DIGITAL TRANSMISSION PIPE
  if(ps2x.ButtonPressed(PSB_TRIANGLE))  transmitToken("TRIANGLE");
  if(ps2x.ButtonReleased(PSB_TRIANGLE)) transmitToken("TRIANGLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CIRCLE))    transmitToken("CIRCLE");
  if(ps2x.ButtonReleased(PSB_CIRCLE))   transmitToken("CIRCLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CROSS))     transmitToken("CROSS");
  if(ps2x.ButtonReleased(PSB_CROSS))    transmitToken("CROSS RELEASED");
  if(ps2x.ButtonPressed(PSB_SQUARE))    transmitToken("SQUARE");
  if(ps2x.ButtonReleased(PSB_SQUARE))   transmitToken("SQUARE RELEASED");

  if(ps2x.ButtonPressed(PSB_START)) {
    runtimeActive = true; 
    transmitToken("START Sketch Version V84");
  }
  if(ps2x.ButtonPressed(PSB_SELECT))    transmitToken("SELECT");
  if(ps2x.ButtonReleased(PSB_SELECT))   transmitToken("SELECT RELEASED");

  if(ps2x.ButtonPressed(PSB_PAD_UP))     transmitToken("PAD UP");
  if(ps2x.ButtonReleased(PSB_PAD_UP))    transmitToken("PAD UP RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_DOWN))   transmitToken("PAD DOWN");
  if(ps2x.ButtonReleased(PSB_PAD_DOWN))  transmitToken("PAD DOWN RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_LEFT))   transmitToken("PAD LEFT");
  if(ps2x.ButtonReleased(PSB_PAD_LEFT))  transmitToken("PAD LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_RIGHT))  transmitToken("PAD RIGHT");
  if(ps2x.ButtonReleased(PSB_PAD_RIGHT)) transmitToken("PAD RIGHT RELEASED");

  if(ps2x.ButtonPressed(PSB_L1))        transmitToken("TOOL ADVANCE");
  if(ps2x.ButtonReleased(PSB_L1))       transmitToken("TOOL ADVANCE RELEASED");
  if(ps2x.ButtonPressed(PSB_L2))        transmitToken("TOOL RETRACT");
  if(ps2x.ButtonReleased(PSB_L2))       transmitToken("TOOL RETRACT RELEASED");
  if(ps2x.ButtonPressed(PSB_R1))        transmitToken("READY POSITION");
  if(ps2x.ButtonReleased(PSB_R1))       transmitToken("READY POSITION RELEASED");
  if(ps2x.ButtonPressed(PSB_R2))        transmitToken("SYSTEM HOME");
  if(ps2x.ButtonReleased(PSB_R2))       transmitToken("SYSTEM HOME RELEASED");

  if(ps2x.ButtonPressed(PSB_L3))        transmitToken("STICK CLICK LEFT");
  if(ps2x.ButtonReleased(PSB_L3))       transmitToken("STICK CLICK LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_R3))        transmitToken("STICK CLICK RIGHT");
  if(ps2x.ButtonReleased(PSB_R3))       transmitToken("STICK CLICK RIGHT RELEASED");

  // ZONE 3: INCOMING SERIAL LISTENER (LIGHTING & TELEMETRY CONTROL)
  if (Serial.available() > 0) {
    String rcved = Serial.readStringUntil('\n');
    rcved.trim();
    
    // Telemetry Gate Control Commands
    // Positive device identification for automatic Python port discovery.
    // This command must not alter LEDs, controller state, or telemetry state.
    if (rcved == "WHOAREYOU") {
      Serial.println("COKOINO CKK0017 V84");
    }
    else if (rcved == "ANALOG:ENABLE") {
      analogEnabled = true;
    }
    else if (rcved == "ANALOG:DISABLE") {
      analogEnabled = false;
    }
    // Error / Alarm Signal
    else if (rcved == "ERROR:OUT_OF_BOUNDS") {
      isBlinkingError = true;
      lastBlinkTime = millis();
      errorLedState = true;
      setLEDs(strip.Color(255, 0, 0));
    }
    // Existing LED commands
    else if (rcved.startsWith("LED:LOCK:")) {
      isBlinkingError = false;
      char hexChar = rcved.charAt(9);
      int count = (hexChar >= 'A') ? (hexChar - 'A' + 10) : (hexChar - '0');
      
      uint32_t activeColor   = strip.Color(0, 0, 255);
      uint32_t inactiveColor = strip.Color(10, 10, 10);

      strip.setPixelColor(3, (count & 1) ? activeColor : inactiveColor);
      strip.setPixelColor(2, (count & 2) ? activeColor : inactiveColor);
      strip.setPixelColor(1, (count & 4) ? activeColor : inactiveColor);
      strip.setPixelColor(0, (count & 8) ? activeColor : inactiveColor);
      strip.show();
    }
    else if (rcved == "LED:STATE:GREEN") {
      isBlinkingError = false;
      setLEDs(strip.Color(0, 255, 0)); 
    }
  }

  // ZONE 4: NON-BLOCKING BLINK ENGINE
  if (isBlinkingError) {
    unsigned long currentMillis = millis();
    if (currentMillis - lastBlinkTime >= BLINK_INTERVAL) {
      lastBlinkTime = currentMillis;
      errorLedState = !errorLedState;
      if (errorLedState) {
        setLEDs(strip.Color(255, 0, 0));
      } else {
        setLEDs(strip.Color(0, 0, 0));
      }
    }
  }
  
  delay(40);
}

This version is to be updated to send an identification string when queried by the Python program.  The purpose of this change is to facilitate operation in the field, without an attached monitor, keyboard or mouse.

(th)

Offline

Like button can go here

#55 2026-09-20 13:22:59

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post is about LynxMotion.

The command language we are using to control the LynxMotion AL5D arm was (apparently) designed by Jim Frye

who designed lynxmotion robotic arm?
AI Overview
The iconic Lynxmotion robotic arm series (such as the AL5 series and Servo Erector Set arms) was designed by Jim Frye, the founder of Lynxmotion.
Key Details


    Founder & Designer: Jim Frye founded Lynxmotion in 1995 and personally engineered and created many of the company's foundational hobbyist and educational robotic arm kits (including early iterations like the Lynx 5

    and 6    , and later the popular AL5A    , AL5B    , and AL5D    models).
    Lynxmotion
    System Philosophy: Frye utilized a modular "Servo Erector Set" (SES) approach, combining low-cost aluminum brackets, tubing, and standard RC servos to build robust, expandable arms.

    Instagram·trossenrobotics +1

    Legacy: RobotShop acquired Lynxmotion in 2012 and has continued evolving the line—including newer SES-V2
    and SES-PRO smart servo arms—while preserving Jim Frye’s original design legacy.

    Lynxmotion +1

    History - Lynxmotion
    Jim Frye Founds Lynxmotion. Lynxmotion was founded In 1995 by Jim Frye (The Robot Guy), due to his fascination with robotics and a...
    Lynxmotion

    About Us - Lynxmotion
    Lynxmotion is a company that makes hobby and research level robot kits and parts. Lynxmotion has been in business since 1995. They...

Lynxmotion
https://www.lynxmotion.com
The Lynxmotion AL5 series robotic arms feature base rotation, single plane shoulder, elbow, wrist motion, a functional gripper, and several optional upgrades

Lynxmotion Robot Arm Review! Precision Robotics from ...
YouTube · Bens Stuff
Jun 24, 2022
YouTube · Bens Stuff
24:07

Lynxmotion Robot Arm Review Part 2! Precision Robotics from ...
YouTube · Bens Stuff
Jul 9, 2022
YouTube · Bens Stuff
17:26
10 key moments in this video

Lynxmotion SES-PRO Modular Robotic Arm by RobotShop.com
YouTube · RobotShopTV
Oct 9, 2024
YouTube · RobotShopTV
0:36
AL5D Robotic Arm | Controlled with Arduino and Custom Library
YouTube · XTheMoose
Jul 10, 2022
YouTube · XTheMoose
13:03
7 key moments in this video

(th)

Offline

Like button can go here

#56 2026-09-27 14:01:13

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post shows a stage in development of stand-alone capability for the robot bridge system we've been working on.

We are testing Version 86 of a Python program to bridge between Cokoino V86 and a LynxMotion AL5D robot arm.

The immediate problem in work is identification of the connected devices when there is no monitor/keyboard/mouse available.

The runs shown below were performed without the LynxMotion powered up, and then with power.

python3 bridgeV86.py

========================================
COKOINO / LYNXMOTION BRIDGE V86
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V86 AUTOMATIC USB IDENTIFICATION TEST
========================================
[DISCOVERY TEST] /dev/ttyUSB0 -> UNKNOWN
[DISCOVERY TEST] /dev/ttyUSB1 -> COKOINO
[DISCOVERY TEST] /dev/ttyUSB2 -> UNKNOWN
========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ # turned LynxMotion on
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV86.py

========================================
COKOINO / LYNXMOTION BRIDGE V86
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V86 AUTOMATIC USB IDENTIFICATION TEST
========================================
[DISCOVERY TEST] /dev/ttyUSB0 -> LYNXMOTION
[DISCOVERY TEST] /dev/ttyUSB1 -> COKOINO
[DISCOVERY TEST] /dev/ttyUSB2 -> UNKNOWN
========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $

(th)

Offline

Like button can go here

#57 2026-09-29 18:25:57

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post contains a transcript of testing of V87 of the python Bridge program to interface between Cokoino CKK0017 and LynxMotion AL5D.

In this test we are looking for the devices that must be present for the program to run.  I ran multiple times to test performance with and without the Win 7 and with and without the LynxMotion powered up.

tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV87.py

========================================
COKOINO / LYNXMOTION BRIDGE V87
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V87 MULTI-PORT ROBOT DISCOVERY TEST
========================================

[DISCOVERY TEST] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : []
  UNKNOWN    : ['/dev/ttyUSB1', '/dev/ttyUSB2']

[DISCOVERY TEST] Identification counts:
  COKOINO count    : 1
  LYNXMOTION count : 0
  UNKNOWN count    : 2

========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ # VER showed up on
tahanson43206@raspberrypi:~/LynxMotion $ # Win 7 as part of discovery sequence
tahanson43206@raspberrypi:~/LynxMotion $ # now testing in stand alone mode
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV87.py

========================================
COKOINO / LYNXMOTION BRIDGE V87
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V87 MULTI-PORT ROBOT DISCOVERY TEST
========================================

[DISCOVERY TEST] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : []
  UNKNOWN    : ['/dev/ttyUSB1', '/dev/ttyUSB2']

[DISCOVERY TEST] Identification counts:
  COKOINO count    : 1
  LYNXMOTION count : 0
  UNKNOWN count    : 2

========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ # now testing with LynxMotion powered up
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV87.py

========================================
COKOINO / LYNXMOTION BRIDGE V87
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V87 MULTI-PORT ROBOT DISCOVERY TEST
========================================

[DISCOVERY TEST] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[DISCOVERY TEST] Identification counts:
  COKOINO count    : 1
  LYNXMOTION count : 1
  UNKNOWN count    : 1

========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ # this time WHOAREYOU showed up on the Win 7 display
tahanson43206@raspberrypi:~/LynxMotion $ Now testing with stand alone
bash: Now: command not found
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV87.py

========================================
COKOINO / LYNXMOTION BRIDGE V87
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V87 MULTI-PORT ROBOT DISCOVERY TEST
========================================

[DISCOVERY TEST] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[DISCOVERY TEST] Identification counts:
  COKOINO count    : 1
  LYNXMOTION count : 1
  UNKNOWN count    : 1

========================================
DISCOVERY TEST COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ #Note that Win 7 shows WHOAREYOUVER
tahanson43206@raspberrypi:~/LynxMotion $ # This may be because no line feed is transmitted.
tahanson43206@raspberrypi:~/LynxMotion $ # my impression is that this was a successful test.
tahanson43206@raspberrypi:~/LynxMotion $

Offline

Like button can go here

#58 2026-09-29 19:38:45

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

V87 test was suiccessful. This report is for V88.  In this test we open the ports after identifying them.

As a reminder for those who may be seeing this report for the first time, we are sending "WHOAREYOU" to the Cokoino, and we are sending VER to the LynxMotion.

========================================
COKOINO / LYNXMOTION BRIDGE V88
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V88 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : []
  UNKNOWN    : ['/dev/ttyUSB1', '/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...
[STARTUP GATE] FAILED: Exactly one LynxMotion SSC-32U is required; found 0.

========================================
V88 STARTUP NOT AUTHORIZED
Required robot hardware was not uniquely identified.
No operational robot ports were opened.
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV88.py

========================================
COKOINO / LYNXMOTION BRIDGE V88
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V88 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : []
  UNKNOWN    : ['/dev/ttyUSB1', '/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...
[STARTUP GATE] FAILED: Exactly one LynxMotion SSC-32U is required; found 0.

========================================
V88 STARTUP NOT AUTHORIZED
Required robot hardware was not uniquely identified.
No operational robot ports were opened.
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV88.py

========================================
COKOINO / LYNXMOTION BRIDGE V88
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V88 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB0
  LYNX_PORT    = /dev/ttyUSB1

[OPEN TEST] Opening operational robot ports...
[OPEN TEST] Cokoino port opened successfully.
[OPEN TEST] LynxMotion port opened successfully.

[OPEN TEST] Test ports closed cleanly.

========================================
V88 STARTUP GATE TEST PASSED
Required robot ports were identified and opened successfully.
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV88.py

========================================
COKOINO / LYNXMOTION BRIDGE V88
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V88 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB0
  LYNX_PORT    = /dev/ttyUSB1

[OPEN TEST] Opening operational robot ports...
[OPEN TEST] Cokoino port opened successfully.
[OPEN TEST] LynxMotion port opened successfully.

[OPEN TEST] Test ports closed cleanly.

========================================
V88 STARTUP GATE TEST PASSED
Required robot ports were identified and opened successfully.
Robot operational code was NOT entered.
========================================

(th)

Offline

Like button can go here

#59 2026-09-30 11:18:40

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post contains the transcript of V89 of a bridge program between Cokoino CKK0017 and LynxMotion AL5D.

This run shows that automatic detection of devices seems to be working. The next step will be to release the program to run beyond the port setup.

python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB0
  LYNX_PORT    = /dev/ttyUSB1

[BENCH] Evaluating optional Windows 7 reporting port...
[BENCH] One unclaimed USB serial device is available for Windows 7 reporting.
[BENCH] WIN7_PORT = /dev/ttyUSB2

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : BENCH
COKOINO_PORT           : /dev/ttyUSB0
LYNX_PORT              : /dev/ttyUSB1
WIN7 reporting enabled : True
WIN7_PORT              : /dev/ttyUSB2
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB0']
  LYNXMOTION : ['/dev/ttyUSB1']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB0
  LYNX_PORT    = /dev/ttyUSB1

[FIELD] Windows 7 reporting disabled.
[FIELD] Unrecognized USB serial devices are ignored.

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : FIELD
COKOINO_PORT           : /dev/ttyUSB0
LYNX_PORT              : /dev/ttyUSB1
WIN7 reporting enabled : False
WIN7_PORT              : None
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================

(th)

Offline

Like button can go here

#60 2026-09-30 12:12:12

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

This post contains a transcript of a run of V89 without the safety checks put in during Port automation.  The run seemed top go well/ Om V90 we will remove both safety checks, adjust the printouts, and add a feature to allow the program to close.  How we will do that has not been decided.  One possibility is to force the program into an error halt. Normally a "Start" would resume operation. However, if we program any ** other ** key to cause a shutdown, that might be a reasonable idea.

python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB1']
  LYNXMOTION : ['/dev/ttyUSB0']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB1
  LYNX_PORT    = /dev/ttyUSB0

[BENCH] Evaluating optional Windows 7 reporting port...
[BENCH] One unclaimed USB serial device is available for Windows 7 reporting.
[BENCH] WIN7_PORT = /dev/ttyUSB2

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : BENCH
COKOINO_PORT           : /dev/ttyUSB1
LYNX_PORT              : /dev/ttyUSB0
WIN7 reporting enabled : True
WIN7_PORT              : /dev/ttyUSB2
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ # that was a surprise
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB1']
  LYNXMOTION : ['/dev/ttyUSB0']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB1
  LYNX_PORT    = /dev/ttyUSB0

[FIELD] Windows 7 reporting disabled.
[FIELD] Unrecognized USB serial devices are ignored.

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : FIELD
COKOINO_PORT           : /dev/ttyUSB1
LYNX_PORT              : /dev/ttyUSB0
WIN7 reporting enabled : False
WIN7_PORT              : None
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================
tahanson43206@raspberrypi:~/LynxMotion $ nano bridgeV89.py
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] SPACE detected.
[MODE] BENCH

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB1']
  LYNXMOTION : ['/dev/ttyUSB0']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB1
  LYNX_PORT    = /dev/ttyUSB0

[BENCH] Evaluating optional Windows 7 reporting port...
[BENCH] One unclaimed USB serial device is available for Windows 7 reporting.
[BENCH] WIN7_PORT = /dev/ttyUSB2

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : BENCH
COKOINO_PORT           : /dev/ttyUSB1
LYNX_PORT              : /dev/ttyUSB0
WIN7 reporting enabled : True
WIN7_PORT              : /dev/ttyUSB2
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================
^C
Bridge safely terminated.
tahanson43206@raspberrypi:~/LynxMotion $ python3 bridgeV89.py

========================================
COKOINO / LYNXMOTION BRIDGE V89
Press SPACE now for BENCH MODE
Default after 5 seconds: FIELD MODE
========================================

[MODE] Startup window expired.
[MODE] FIELD

========================================
V89 STARTUP SAFETY GATE TEST
========================================

[DISCOVERY] Positive identification results:
  COKOINO    : ['/dev/ttyUSB1']
  LYNXMOTION : ['/dev/ttyUSB0']
  UNKNOWN    : ['/dev/ttyUSB2']

[STARTUP GATE] Validating required robot devices...

[STARTUP GATE] Required devices uniquely identified.
  COKOINO_PORT = /dev/ttyUSB1
  LYNX_PORT    = /dev/ttyUSB0

[FIELD] Windows 7 reporting disabled.
[FIELD] Unrecognized USB serial devices are ignored.

========================================
V89 FINAL PORT CONFIGURATION
========================================
Operating mode         : FIELD
COKOINO_PORT           : /dev/ttyUSB1
LYNX_PORT              : /dev/ttyUSB0
WIN7 reporting enabled : False
WIN7_PORT              : None
Unclaimed USB devices  : ['/dev/ttyUSB2']
========================================

V89 CONFIGURATION CHECKPOINT COMPLETE
Robot operational code was NOT entered.
========================================
^C
Bridge safely terminated.
tahanson43206@raspberrypi:~/LynxMotion $ # notes for V90
tahanson43206@raspberrypi:~/LynxMotion $ # Found second exit
tahanson43206@raspberrypi:~/LynxMotion $ # found text in print that is not accurate
tahanson43206@raspberrypi:~/LynxMotion $ # need way to stop run
tahanson43206@raspberrypi:~/LynxMotion $ # currently using Ctrl-C at kehboard
tahanson43206@raspberrypi:~/LynxMotion $

(th)

Offline

Like button can go here

#61 2026-10-01 06:55:45

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

Version 91 of our Python bridge program to interface a Cokoino CKK0017 controller to a LynxMotion AD5D robot arm is ready for test. This is an incremental update that adds a disk logging service to the program. We are planning to operate in "headless" mode, with no monitor, keyboard or mouse.  A key requirement is to be able to know what happened when failures occur.  The new logging feature is being added before any processing is attempted, so the log can report any failures during startup.

Failures during startup are inevitable, because devices must be connected and powered up.  the Raspberry Pi has no signalling capability of it's own, so the log is the only information source we can study.

(th)

Offline

Like button can go here

#62 2026-10-01 19:31:04

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

Version 92 of the Python program to bridge between Cokoino CKK0017 and LynxMotion AL5D passed testing of headless operation today.

The Linux operating system was given a command to start the program as a service, and that worked as intended.

With no devices connected the program shut down.

With one device connected the program shut down.

With both devices connected the program opened sessions with both devices and performed normal operations.

In tomorrow's work session we will set up the system daemon to run at boot, and we will add code to allow the operator to shut down the entire system using a combination key command.  This is necessary because Linux needs to be shutdown properly, unlike simple machines like Cokoino or LynxMotion.

A demo is scheduled for Saturday afternoon, so I've been pushing to see if ChatGPT and I could finish updates no later than Friday.

(th)

Offline

Like button can go here

#63 2026-10-03 19:17:14

tahanson43206
Moderator
Registered: 2018-04-27
Posts: 26,086

Re: Robotics Education Root Topic

The Cokoino sketch for the bridge project has been stable for some time, but there may be small changes that are present in the current version that were added to the last version stored here.

this is Version 97 of the Cokoino sketch that works with Version 97 of the Python script that handles headless operation.

The two were deployed today in a successful demonstration of the working system in a public setting.

// CokoinoV97.ino Prepared by Gemini Supervised by Tom Hanson
// ChatGPT will handle updates from here on. 
// Version 69: Added non-blocking ERROR:OUT_OF_BOUNDS blinking red LED state.
// Version 68: Updated START token version string to V68.
// Version 62: No change except version number Note Ready: version
// Version 59: Updated version strings for V59 integration with Gated Telemetry handshake.
// Version 58: Introduced Gated Telemetry to completely eliminate analog line chatter.

#include <PS2X_lib.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN A1
#define LED_COUNT 4
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

PS2X ps2x;
int error = 0;
bool runtimeActive = false;

// --- GATED TELEMETRY STATE ---
bool analogEnabled = false; // Muted by default to keep the transmission pipe silent

// --- ERROR / ALARM STATE ---
bool isBlinkingError = false;
unsigned long lastBlinkTime = 0;
bool errorLedState = false;
const unsigned long BLINK_INTERVAL = 250; // 250ms toggle rate (4Hz blink)

void setLEDs(uint32_t c) {
  for(int i=0; i<LED_COUNT; i++) strip.setPixelColor(i, c);
  strip.show();
}

void transmitToken(String token) {
  // Clearing error state on explicit user action/reset tokens
  if (token == "START Sketch Version V97" || token == "TRIANGLE" || token == "CIRCLE") {
    isBlinkingError = false;
  }

  if (!runtimeActive) {
    setLEDs(strip.Color(255, 150, 0)); // Yellow signal on key test
  }
  
  Serial.println(token);
  
  if (!runtimeActive) {
    delay(60); 
    setLEDs(strip.Color(0, 255, 0));   // Green = Standby
  }
}

void setup(){
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(40);
  strip.show(); 

  error = ps2x.config_gamepad(10, 12, 11, 13);
  if(error == 0) Serial.println("V97 Ready: Pass-Through Mode Enabled.");
}

void loop(){
  if(error != 0) return;
  
  ps2x.read_gamepad(false, 0);

  // ZONE 1: ANALOG STREAMING ENGINE (GATED PASS-THROUGH)
  if (analogEnabled) {
    int lx = ps2x.Analog(PSS_LX);
    int ly = ps2x.Analog(PSS_LY);
    int rx = ps2x.Analog(PSS_RX);
    int ry = ps2x.Analog(PSS_RY);
    
    // Smooth 0-255 pass-through active ONLY when steering mode is live
    if (abs(lx - 128) > 15) { Serial.print("ANALOG:LX:"); Serial.println(lx); }
    if (abs(ly - 128) > 15) { Serial.print("ANALOG:LY:"); Serial.println(ly); }
    if (abs(rx - 128) > 15) { Serial.print("ANALOG:RX:"); Serial.println(rx); }
    if (abs(ry - 128) > 15) { Serial.print("ANALOG:RY:"); Serial.println(ry); }
  }

  // ZONE 2: DIGITAL TRANSMISSION PIPE
  if(ps2x.ButtonPressed(PSB_TRIANGLE))  transmitToken("TRIANGLE");
  if(ps2x.ButtonReleased(PSB_TRIANGLE)) transmitToken("TRIANGLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CIRCLE))    transmitToken("CIRCLE");
  if(ps2x.ButtonReleased(PSB_CIRCLE))   transmitToken("CIRCLE RELEASED");
  if(ps2x.ButtonPressed(PSB_CROSS))     transmitToken("CROSS");
  if(ps2x.ButtonReleased(PSB_CROSS))    transmitToken("CROSS RELEASED");
  if(ps2x.ButtonPressed(PSB_SQUARE))    transmitToken("SQUARE");
  if(ps2x.ButtonReleased(PSB_SQUARE))   transmitToken("SQUARE RELEASED");

  if(ps2x.ButtonPressed(PSB_START)) {
    runtimeActive = true; 
    transmitToken("START Sketch Version V97");
  }
  if(ps2x.ButtonPressed(PSB_SELECT))    transmitToken("SELECT");
  if(ps2x.ButtonReleased(PSB_SELECT))   transmitToken("SELECT RELEASED");

  if(ps2x.ButtonPressed(PSB_PAD_UP))     transmitToken("PAD UP");
  if(ps2x.ButtonReleased(PSB_PAD_UP))    transmitToken("PAD UP RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_DOWN))   transmitToken("PAD DOWN");
  if(ps2x.ButtonReleased(PSB_PAD_DOWN))  transmitToken("PAD DOWN RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_LEFT))   transmitToken("PAD LEFT");
  if(ps2x.ButtonReleased(PSB_PAD_LEFT))  transmitToken("PAD LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_PAD_RIGHT))  transmitToken("PAD RIGHT");
  if(ps2x.ButtonReleased(PSB_PAD_RIGHT)) transmitToken("PAD RIGHT RELEASED");

  if(ps2x.ButtonPressed(PSB_L1))        transmitToken("TOOL ADVANCE");
  if(ps2x.ButtonReleased(PSB_L1))       transmitToken("TOOL ADVANCE RELEASED");
  if(ps2x.ButtonPressed(PSB_L2))        transmitToken("TOOL RETRACT");
  if(ps2x.ButtonReleased(PSB_L2))       transmitToken("TOOL RETRACT RELEASED");
  if(ps2x.ButtonPressed(PSB_R1))        transmitToken("READY POSITION");
  if(ps2x.ButtonReleased(PSB_R1))       transmitToken("READY POSITION RELEASED");
  if(ps2x.ButtonPressed(PSB_R2))        transmitToken("SYSTEM HOME");
  if(ps2x.ButtonReleased(PSB_R2))       transmitToken("SYSTEM HOME RELEASED");

  if(ps2x.ButtonPressed(PSB_L3))        transmitToken("STICK CLICK LEFT");
  if(ps2x.ButtonReleased(PSB_L3))       transmitToken("STICK CLICK LEFT RELEASED");
  if(ps2x.ButtonPressed(PSB_R3))        transmitToken("STICK CLICK RIGHT");
  if(ps2x.ButtonReleased(PSB_R3))       transmitToken("STICK CLICK RIGHT RELEASED");

  // ZONE 3: INCOMING SERIAL LISTENER (LIGHTING & TELEMETRY CONTROL)
  if (Serial.available() > 0) {
    String rcved = Serial.readStringUntil('\n');
    rcved.trim();
    
    // Telemetry Gate Control Commands
    // Positive device identification for automatic Python port discovery.
    // This command must not alter LEDs, controller state, or telemetry state.
    if (rcved == "WHOAREYOU") {
      Serial.println("COKOINO CKK0017 V97");
    }
    else if (rcved == "ANALOG:ENABLE") {
      analogEnabled = true;
    }
    else if (rcved == "ANALOG:DISABLE") {
      analogEnabled = false;
    }
    // Error / Alarm Signal
    else if (rcved == "ERROR:OUT_OF_BOUNDS") {
      isBlinkingError = true;
      lastBlinkTime = millis();
      errorLedState = true;
      setLEDs(strip.Color(255, 0, 0));
    }
    // Existing LED commands
    else if (rcved.startsWith("LED:LOCK:")) {
      isBlinkingError = false;
      char hexChar = rcved.charAt(9);
      int count = (hexChar >= 'A') ? (hexChar - 'A' + 10) : (hexChar - '0');
      
      uint32_t activeColor   = strip.Color(0, 0, 255);
      uint32_t inactiveColor = strip.Color(10, 10, 10);

      strip.setPixelColor(3, (count & 1) ? activeColor : inactiveColor);
      strip.setPixelColor(2, (count & 2) ? activeColor : inactiveColor);
      strip.setPixelColor(1, (count & 4) ? activeColor : inactiveColor);
      strip.setPixelColor(0, (count & 8) ? activeColor : inactiveColor);
      strip.show();
    }
    else if (rcved == "LED:STATE:GREEN") {
      isBlinkingError = false;
      setLEDs(strip.Color(0, 255, 0)); 
    }
  }

  // ZONE 4: NON-BLOCKING BLINK ENGINE
  if (isBlinkingError) {
    unsigned long currentMillis = millis();
    if (currentMillis - lastBlinkTime >= BLINK_INTERVAL) {
      lastBlinkTime = currentMillis;
      errorLedState = !errorLedState;
      if (errorLedState) {
        setLEDs(strip.Color(255, 0, 0));
      } else {
        setLEDs(strip.Color(0, 0, 0));
      }
    }
  }
  
  delay(40);
}

(th)

Offline

Like button can go here

Board footer

Powered by FluxBB