Showing posts with label R12. Show all posts
Showing posts with label R12. Show all posts

Thursday, March 12, 2026

ST-Robotics R12 arm - IO ports and expansion ports

IO ports 

Official doc

After doing all the analysis below, I found that much of the information below, plus further info about the PB port, is tucked away in the Help Sheets/HELP11 How to use the I-O.pdf file.  Still, there are some things that I have here that aren't shown in the help file, including actual pin positions.  I guess in my stubborn way of looking at things, I have come to think of "Help" files differently from "Manuals" so I just wasn't expecting to find much there.  But there is quite a bit of information in those files.

Output and Input Ports (PA and PB)

The controller comes with OUTPUT and INPUT ports.  These correspond to the terms PA and PB in the documentation.  OUTPUT (PA) is a Dsub 15 pin female port on the back, and INPUT (PB) is a Dsub 9 male port.  Both ports are in a horizontal orientation.

OUTPUT port is PA.  INPUT port is PB.

 

Note that in standard Dsub 15 pin numbering, the female pins are read right-to-left, and the male port pins are read left-to-right.

OUTPUT (PA) port pins

  8   7   6   5   4   3   2   1

  o   o   o   o   o   o   o   o

    o   o   o   o   o   o   o

    15  14  13  12  11  10  9

    +   +   +   +   +   -   - 

 


 

INPUT (PB) port pins

     1   2   3   4   5

  .   .   .   .   .

    .   .   .   .

    6   7   8   9

Inner workings

Standard K11R IO board.  The labels 3A..3E are my own terms.

 

Internally, the first row of PA pins connect to two ULN2068B devices.  They are socketed, which is nice from a maintenance standpoint.  The "u" notch is positioned pointing towards the connector header on the board, which then goes to the exterior port on a ribbon cable.

OUTPUT (PA) port corresponding to 3e above. Darlington C lines connect to port pins.

The ULN2068B is a quad-Darlington pair device.  Each Darlington pair has K, C, and B pins.  C is the collector, and B is the base.

The base pin of each Darlington pair goes through a resistor array and back to the controller somewhere.

Each collector (C) pin is connected to one of the PA port input pins.  The physical positioning of each device allows easy connection of pins 8, 2, 16, and 10 to one set of header pins, and in the same order, 8, 2, 16, and 10 to the other set of header pins.  As such, the first device's four collectors go to pins 1, 2, 3, and 4, and the other device's four go to pins 5, 6, 7, and 8. 

Continuity testing between Darlington socket pins and external port pins
 

The device's B connections aren't obvious, but I was able to test connectivity by removing the chips from their sockets and doing a continuity test between the socket points and the resistor array points. 

The output port's lower pins are set up as V+ (pins 15, 14, 13, 12, 11), and ground (GND) (pins 10, 9).  As expected, the associated wires are colored red and black on the corresponding internal cable.

Measuring the voltage output across pins 9 and 14, for example, yields +12VDC.

For testing, I initially used a step-down buck converter to bring the +12VDC down to +5VDC, and then sent the 5v through 300 ohms to an LED.  But a simpler solution is just to use a larger resistor, and skip the buck converter.

The test circuit on each pin is:

For each pin i in range (1..8)

+12v (9) -> 675 ohms -> LED+ -> LED- -> PA pin i

All eight pins share the same +12v output from one of the V+ pins, but each has its own path to ground.

The LED that I used has a voltage drop of 1.2V, so the current per LED is (12 - 1.2) / 675 == 0.016 A (16 mA). 

NB: the GND pins on the PA port appear to connect to case ground.

Software activation of PA pins 

To activate an output pin (i.e., to allow the flow of current through a collector), there are convenience functions in ROBOFORTH called PA n ON or PA n OFF.  Note that the n value here is in the range 0..7.

Using the above test circuit, you can say "PA 1 ON" or "PA 3 ON", and one of the LEDs will be lit. 

These functions for PA n ON/OFF provide control of individual pins, one at a time.  Later for the MultiIOcard, we'll see that QA, QB, and QC pin references are done in a different, binary way, where all pins can be set in one operation. 

The R12 manual (page 6) has an odd warning, saying, "The gripper drive module uses PA 0 and PA 1 so PA 1 should not be used for any other purpose".  However, it does not associate PA 0 nor PA 1 with physical pins.  One page 14, it only shows connections between pins 8 (black) and 11 (red).  We now see that what they're describing is a device the gripper is provided +12v on pin 11, and it drains back to pin 8 when the corresponding ULN2068B base pin is activated by saying "PA 0 ON".

I think that if you don't have a standard gripper end-effector, and don't use the built-in GRIP and UNGRIP functions, then the restriction on PA 0 and PA 1 usage isn't applicable.

With those, we find these pin mappings

ON = Darlington base enabled
OFF = Darlington base not enabled
 
PA ON/OFF OUTPUT port pin
   0        8
   1        7
   2        6
   3        5
   4        4
   5        3
   6        2 
   7        1 
V+ (12V)    11, 12, 13, 14, 15
GND         9, 10 

The following is a picture of my PA port tester, missing one LED.  This is the older version, where I was using a stepdown voltage converter to drop the 12v down to 5v to light the LEDs.  In a later form, I bumped up the resistors to I could power them directly from 12v.

In this example, I think I had issued commands PA 0 ON, PA 1 ON, PA 3 ON, and PA 7 ON.  Note how those are read from left to right visually, but by standard pin numbering, those are pins 8, 7, 5, and 1.



 

GRIP, UNGRIP, and TON

As mentioned earlier, GRIP and UNGRIP operate using PA 0 and PA 1.  With the LED test circuit in place, we can now observe that.  Issuing GRIP causes PA 0's LED to turn on and stay on.  UNGRIP lights PA 1 for a short period, and then both PA 0 and PA 1 are turned off.  There isn't a visible dimness to the light, but it may be that not be very noticeable.

The R12 manual, page 17, says, "During closing PA 0 is switched on and off at approx 500Hz.  Once closed PA is left full on."   So I think basically they're saying that they're using PWM to apply a 50% duty cycle (they don't specifically say whether it's on and off evenly during closure) to the PA 0 pin, and thus the grip motor closes more gently.  Upon reaching closure (how is that defined?) it holds at a 100% duty cycle.

Then, it says that "UNGRIP asserts PA 0 off and PA 1 on again at 500 Hz until the gripper is open then asserts PA 1 off".  So I take that to mean that PA 1 is driving a reverse motor gently while opening, and upon having the gripper open (how is that defined?) it turns PA 1 off again. 

The GRIPCHECK command appears to be tied to PB6, so that's reserved, too, in a way. 

In my testing, I had nothing connected to PB6.  Presumably, that's the pin that's being used to know whether the motor is closed or open in some way.  With PB6 disconnected in my case, the GRIP and UNGRIP operations still worked.

Later, on page 17, it introduces a few more variables.  TGRIP is the "time allowed for GRIP and UNGRIP".  It doesn't say what happens if the operation fails to grip or ungrip in the allowed time.

It also says that TON (meaning "time on") allows you to change the 500 Hz timing (by issuing n TON !).  A higher value causes a faster grip.  I suspect this means that TON is changing the PWM duty cycle.  The document does not describe min or max values for TON.

ROBOFORTH does not present a corresponding TOFF variable.

It's not clear if there's any other way to control PWM on any other PA pins than PA 0. 

The tutorial5-axis.pdf document also says that the commands GRIPPER ON and GRIPPER OFF can be used to activate the "pneumatic (not electric) gripper without any delay".

Further, the tutorial5-axis.pdf document says that the "Normal value for TON is 1000 so power is on for 1000 and off for 1000 uSecs".  This is a very odd way to describe PWM behavior, but it suggests that the max value for TON should be 2000 and the duty cycle is TON / 2000.  But, I can only really understand the behavior by analyzing it with an oscilloscope.  The description that the "power is on" for 1000 uSecs and off for 1000 uSecs sounds like it goes on for one second, and then off for one second.  It'd be nice if ST-Robotics could provide a graph of the waveform (ideally screen-captured from an oscilloscope) so we can get a better understanding of what's going on.

Still, all that is really only relevant if you have either of the electric or pneumatic gripper modules, and I do not.  I may, though, try to build one of my own, once I figure out how these actually behave. 

Actual GRIP, TON, and TGRIP PWM behavior

I hooked up a Tektronix 465 scope and took a bunch of video captures of signals, varying the TON value.  It turns out that it's a pretty simple concept that has a simpler explanation.

The following pictures are measuring PA 0.  Since that's a Darlington collector pin, it's ACTIVE LOW.

1ms resolution, TON set to 2000

 
TON set to 3000 (3ms on, 1 ms off)

TON set to 500 (0.5ms on, 1 ms off)

Resolution 2ms.  TON set to 7000 (7ms on, 1 ms off)

It appears that the GRIP function is made active at a PWM power mode for a total time of TGRIP (in milliseconds, I think).  The default value for TGRIP is 500, so it's given a half second to grab slowly, and then it goes to full grab power.

Within the TGRIP amount of time, it does PWM, cycling the circuit for TON microseconds enabled (allows current flow through the collector) and then 1000 microseconds disabled.  Here are some duty cycle examples

TON setting  Active      Inactive     Duty cycle
500          500 usecs   1000 usecs   33%
1000        1000 usecs   1000 usecs   50%
2000        2000 usecs   1000 usecs   66%
7000        7000 usecs   1000 usecs   87.5% (7/8ths) 

Clearly, the formula is: duty cycle = TON / (TON + 1000)

This applies to PA 0, pin 1 of the OUTPUT port (Dsub 15).

At power-up, my controller had TON set to 7000.

To set a TON value, use the command

n TON ! ( e.g., 4000 TON ! )1

To inspect the TON value, use the command

> TON @ .

I would guess that the maximum value you could assign to TON is 65535 since it appears to be dealing with a 16-bit quantity.  It's not clear if the value would be flipped negative at the 32768 point (i.e., it's not clear if it was properly implemented as an unsigned int16 as opposed to a signed int16).  Practically speaking, it shouldn't matter, given its purpose. 

Naming conventions

As it turns out, there is a bit of a hidden naming convention in the IO pins.  "A" is for output, and "B" is for input.  There's also "C" that has half input and half output, as we'll see later.

PB wiring

I have not yet tested these inputs.

MultiIOcard ports


MultiIO card, viewed from front of K11R.  J4,J5,J6 at top.  J2, J3 on right.

 

The MultiIO card, once wired up, gives you access to potentially three more Dsub25 "digital" male ports, plus an additional Dsub25 Analog female port.  Mine only came partially configured, so there are only two of the Dsub25 "digital" male ports.

The MultiIO card (see MultiIOcard.pdf)  is similar to what is depicted in MultiIOcard.pdf, page 2, but in my controller, the layout is a bit different.  The Gecko drives are positioned underneath a stack of cards that share a bus, and the boards are rotated 90 degrees clockwise compared to the picture.

Still, the junctions are as depicted.  J4 connects to the port labeled QQ, and J5 connects to QR.  I do not have the large IC shown in the upper part of the board, and I do not have a connector or Dsub25 represented as RR.


 

The analog Dsub25 female, AN, is connected to junction points J2 and J3.

QQ was labeled, but QR was not on the outside of my controller box.

AN and QQ ports on right.  QR port is in-between.  My controller does not have an RR port.

 

Naming conventions

As it turns out, the names QQ, QR, and RR are associated to halves of the Dsub25 for different purposes.  QQ (J4) handles the QB and QC pins.  QR (J5) is meant to be shared between QA pins and RB pins, though for my controller, the "R" bank isn't working, presumably because that IC is missing from the board.  RR would then represent the RA and RC pins.

This mapping of QA,QB,QC and RA,RB,RC can be found on page 4 of the MultiIOcard.pdf document.

As mentioned earlier, "A" means output, "B" means input, and "C" provides 4 pins for input, and 4 for output.

The physical-to-logical layout of the pins is described on page 4 as well.  But, the indication of "which pins on QC are for what purpose" isn't described.

Testing the QQ and RR ports

The MultIOcard.pdf document page 3 shows a sequence of commands used to test the proper output activity on QQ and RR, but doesn't really describe what's happening behind the scenes, nor does it describe how to test the input pins.  You first run a "PROGPIA" command.  It's not clear what that does, but it appears to activate the MultiIOcard.  Then, you run the "HEX" command which enables dictionary words 00..FF.  With that, you can use the command hh QA OUT to send a hex byte hh to the port.  To read the value back in, you use QA IN X. (declare QA as the port you want, IN pulls the data byte from it, and X. prints the value in hexadecimal).

If the value written matches the value pulled back in, then the chip is working.

Even with that test defined, you may want to test for a valid connection from there through the HE headers to the actual output Dsub25 pins. 

Software activation of QA pins 

Unlike the PA port, where the port activation opens up a collector to ground, the QA output ports actually emit +5v when activated, and 0v when not activated.  

The pin table shown on page 4 of MultiIOcard.pdf does not show the association of QA bits to corresponding QA0..QA7 references. 

To work with the QA output pins, you can use the n QA OUT command.  This can be done in decimal values, but to me it's more easily "visualized" using hex values.  So, I prefer to say something like

> PROGPIA

> HEX

> AA QA OUT

> 55 QA OUT

> 1C QA OUT

Similar applies for the QC port, but only the highest 4 bits are used for output (0x80, 0x40, 0x20, and 0x10 bits, combined with OR as desired).

> DECIMAL

Software input on QB pins

The input ports can be read as an entire byte, or using individual pin references.

To read a full byte, you can use the IN and X. commands as shown earlier:

> QB IN ( get a value from QB onto the stack )

> QB IN X. ( get the value from QB and print it in hexadecimal format )

To read individual bits, you can AND the values with a bit mask

> DECIMAL

> QB IN 16 AND .

allowing you to test an individual bit or a set of bits at once. 

The document tutorial5-axis.pdf, page 50, also shows that the BIT? operation can be used for individual bit tests.

> QB 5 BIT? . 32 OK 

You can also do a WAIT operation to loop until you see a transition from 0 to 1, or 1 to 0, e.g.,

> QB 5 1 WAIT ( wait for bit 5 to transition from 0 to 1 )

I have not tested the WAIT operation yet. 

Note that for both the BIT? and WAIT operations, the bit number (e.g., 5 above) is in the 0..7 numeric range, not 1..8.  Also, the WAIT operation has a built-in STOP check.

Danger

The pins for each of the Dsub25 connections does not appear to go through a socketed device, as was done for PA.  It also does not appear to have any optoisolation as might be found on an IO1 card (see IO1 card.pdf).  One safety approach that might be worth pursuing is to create an opto-isolation board for QB and QC inputs (12 of the pins of QQ/J4).

The headers for Dsub25 for each of QQ, QR, and RR, are physically the same.  However, their purposes are quite different.

Comparing the "first" 8 pins of each Dsub25 (pins 1, 14, 2, 15, 3, 16, 4, 17): 

QQ (J4) pins are "B" inputs QB0..QB7

QR (J5) pins are "A" outputs QA0..QA7

RR (J6) pins are "C" ins and outs RC0..RC7

Because of this, be very careful in what you attach to the pins.  A given test circuit for one has the potential to blow up another.

Electrical characteristics 

The pin limits are not described.  For example, it's not clear what the max current draw is for the QA output pins.  I've made the assumption that they're about as limited as an Arduino, so my test circuits have LEDs with 330 ohm resistors in series, and that has worked so far.

As with the input pins, it may be a good idea to opto-isolate external circuitry from the QA pins.  Since I can light up a visible LED, it's doable to light up the internal LED of an optoisolator. 

Input pins test circuit

For the input pins on QB and QC, I set up a test circuit:

Input pin ---+--> 1k resistor -> +5v
             |
             +------ pin header  gap  pin header ----> GND

Each pin is pulled high to 5v, and if a jumper is added across the two header pins, the value goes low.  As such, this is inverse logic.  By default, QB pin values, using this circuit, yields a 1 bit, and adding the jumper yields a 0 bit.

These pins were set up on a board matching the QB pins: 1, 14, 2, 15, 3, 16, 4, 17.

Test: jumper on pin 1

> QB IN X. 00FE OK

Test: jumper on pin 2

> QB IN X. 00FB OK

Test: jumper on pin 7

> QB IN X. 007F OK

Output pins test circuit

For the eight QA pins and the upper four QC pins, the output test circuit is similar to what was used for PA, except that we don't need the step-down converter, because the voltage supplied on the output pin is already at +5v.

QR port (J5), for each PIN_i (1, 14, 2, 15, 3, 16, 4, 17, aka QA 1..8):

PIN_i -> 330 ohms -> LED+ -> LED- -> GND (pin 18)

The ground line is shared across all LED cathodes.

QQ port (J4), for each PIN_i (8, 21, 9, 22, aka QC 5..8): 

PIN_i -> 330 ohms -> LED+ -> LED- -> GND (pin 18)

Turning a QA or QC pin on turns the corresponding LED light on.

See mapping below for relationship of QA and QC pins to Dsub25 pins. 

QA port and pin associations

NB: QR is the Dsub25 that correlates to J5.  The first set of pins is for QA, and the latter set is for RB, but my MultiIO card was not configured to support the R pins, or its corresponding IC was removed for some reason. 

NB: Examples of "n QA OUT" shown below must be preceded by the commands PROGPIA and HEX.  You can, of course, use decimal equivalent values instead and use the command DECIMAL instead of HEX. 

QR (Dsub25) pin  QA bit  Set pin to +5v
                         > PROGPIA
                         > HEX
 1               0x01    1 QA OUT
14               0x02    2 QA OUT
 2               0x04    4 QA OUT
15               0x08    8 QA OUT
 3               0x10    10 QA OUT
16               0x20    20 QA OUT
 4               0x40    40 QA OUT
17               0x80    80 QA OUT

NB: +5v is on pin 5, GND is on pin 18 (as with all QQ, QR, RR ports, if wired properly).


 

QB port and pin associations

NB: QQ is the Dsub25 that correlates to J4 

                                          (Decimal)       Both BIT? and AND
QQ (Dsub25) pin  QB bit  BIT? equivalent  AND equivalent  result value
 1               0x01    QB 0 BIT?        QB IN 1 AND     1 or 0
14               0x02    QB 1 BIT?        QB IN 2 AND     2 or 0
 2               0x04    QB 2 BIT?        QB IN 4 AND     4 or 0
15               0x08    QB 3 BIT?        QB IN 8 AND     ...
 3               0x10    QB 4 BIT?        QB IN 16 AND
16               0x20    QB 5 BIT?        QB IN 32 AND
 4               0x40    QB 6 BIT?        QB IN 64 AND
17               0x80    QB 7 BIT?        QB IN 128 AND   128 or 0

The images below were testing QB connections by connecting jumpers from a sense pin to ground.  When there is no connection, the pin is pulled high via the black resistor array.
 
In this example, the value of QB is FA, or binary 1111 1010.  The jumpers are pins 1 and 3, representing the 2^0 and 2^2 bits.
 
In the next example, the jumpers are on pins 4 and 7, and we get back a value of B7 from QB.  In binary, that's 1011 0111, so pins 2^3 and 2^6 are low.
 
One more example: QB = 7D or 0111 1101 when pins 2 and 8 (2^1 and 2^7) are low.

 

QC port and pin associations

QC (Dsub25) input pins behave similarly to QA and QB, but the "first" four pins are inputs, and the "second" four are outputs.
NB: there's a skip in the pin numbering here relative to the end of QB, because 5 is +5V and 18 is GND
NB: QQ is the Dsub25 that correlates to J4 
NB: for testing QC, I used the +5v on pin 10, and the GND line on pin 23 instead of similar pins 5 and 18, so that 5 and 18 could be used for QB testing. Refer to MultiIOcard.pdf (aka Multi-function board 11-48A) page 4 for pin QQ/QR/RR pin names. 
                                          (Decimal)       Both BIT? and AND
QQ (Dsub25) pin  QC bit  BIT? equivalent  AND equivalent  result value
 6               0x01    QC 0 BIT?        QC IN 1 AND     1 or 0
19               0x02    QC 1 BIT?        QC IN 2 AND     2 or 0
 7               0x04    QC 2 BIT?        QC IN 4 AND     4 or 0
20               0x08    QC 3 BIT?        QC IN 8 AND     8 or 0

The remaining QC pins are outputs similar to how J
 
QQ (Dsub25) pin  QC bit  Set pin to +5v
                         > PROGPIA
                         > HEX
 8               0x10    10 QC OUT
21               0x20    20 QC OUT
 9               0x40    40 QC OUT
22               0x80    80 QC OUT

Analog port

The analog port is a Dsub25 female port, oriented vertically, and positioned near the OUTPUT (PA) and INPUT (PB) ports.  

It appears to be connected to the J2 and J3 junctions on the MultiIO card.  Their function is not well described.  

The tutorial5-axis.pdf (page 50) says:

When fitted there are 4 input channels to the ADC 0-3 and 2 DAC output channels A and
B. To read an input
0 ADC reads channel 0 and leaves the value on the stack.
0 DACA sends 0 volts out of DAC channel A
 

and then MultiIOcard.pdf has this brief blurb for testing:

Quick test of the analog input-output
Connect AN connector pins 2 and 8 (DACA to ADC channel 0)
Enter
0 DACA 0 ADC . - answer should be roughly zero
1000 DACA 0 ADC . – answer should be roughly 1000
etc.
 

Based on the descriptions, it seems that the ROBOFORTH variable name patterns are

DACA

DACB

0 ADC

1 ADC

2 ADC

3 ADC

 Near J2 and J3, there are three socketed devices.  All three are labeled 44AE0RG3 / LF / 412CN.

The closest I can find is a Texas Instruments device called LF412CN, "Low Offset, Low Drift Dual JFET Input Operational Amplifier".  Its Applications suggested list includes "Fast D/A Converters".

Each LF412CN has two pairs of inputs (one inverting, one not) and two outputs.  Thus it makes sense that having three of these would cover the four ADC plus two DAC functions above.

NOTE: Digikey says some versions of the LF412CN are obsolete. Presumably, the one I saw was obsoleted due to Pb content, given the name of this suggested substitute: https://www.digikey.com/en/products/detail/texas-instruments/LF412CN-NOPB/8893

The device pins are

1 OUTPUT A

2 INVERTING INPUT A

3 NON-INVERTING INPUT A

4 V-

5 NON-INVERTING INPUT B

6 INVERTING INPUT B

7 OUTPUT B

8 V+ 

Dsub 25 pin connections to op amps (LF412CN) 

Since these are socketed, I removed the devices and found the following with just continuity testing.  (ASCII depiction of Dsub 25 female port pins, port labeled AN.)

                    o  13 NC?

            25  o  

                    o  12 NC?

            24  o  

                    o  11 NC?

            23  o  

                    o  10 GND

            22  o  

                    o  9  GND

            21  o  

                    o  8  U3_7 (OUTPUT B), and R1 to ?

            20  o  

                    o  7  U3_1 (OUTPUT A), and R2 to ?

            19  o  

                    o  6  GND

            18  o  

                    o  5  U5_5 (NON-INV INPUT B), and R8 (1M) to GND

            17  o  

                    o  4  U5_3 (NON-INV INPUT A), and R7 (1M) to GND

            16  o  

                    o  3  U4_5 (NON-INV INPUT A), and R6 (1M) to GND

            15  o  

                    o  2  U4_3 (NON-INV INPUT A), and R5 (1M) to GND

            14  o

                    o  1  GND

All pins 14..25 tone to GND

All GND lines are common, connecting to case GND.

R5..R8 are 1 M ohm.

Resistances for R1 and R2 are not yet measured, and it's not known what else they connect to, but it's not GND.

LF412CN and HE and AN pin associations 

U4 and U5 are the LF412CN devices for the J2 socket.  

 Socket numbering, my way, J2 and J3 connector -- note 'V' key position.  J2 corresponds to U3 and U4.  J3 corresponds to U5.

                 +-------+ 
                 |       |
       +---------+       +---V-----+
       |   5    4    3    2    1   |
       |   o    o    o    o    o   |
       |                           |
       |   o    o    o    o    o   |
       |  10    9    8    7    6   |
       +---------------------------+ 

The associations between J2 pins and AN pins is

J2  AN
 5   1 GND
 1  14 GND
 4   2 U4_3 + pulldown
 9  15 GND
 3   3 U4_5 + pulldown
 8  16 GND
 2   4 U5_3 + pulldown
 7  17 GND
 1   5 U5_5 + pulldown
 6  18 GND

 The associations between J3 pins and AN pins is

J3  AN
 5  NC GND
 1  19 GND
 4   7 U3_1 + resistor + ?
 9  20 GND
 3   8 U3_7 + resistor + ?
 8  21 GND
 2   9 GND
 7  22 GND
 1  10 GND
 6  23 GND 

More internal devices

On the J2/U4/U5 side of the board, there is a device labeled ADS7824P.  It has a "BB" logo, which correlates to an online data sheet that says "Burr-Brown (TI)".  That's a 4-channel, A/D converter.  

On the J3/U3 side of the boards, there is a device labeled AD7237 JNZ / #1513 / AL83868, a "Dual 12-bit complete DAC with double buffered byte loading".

Analysis 

The documentation says, "there are 4 input channels to the ADC 0-3".  Given we have four pins on J2/U4/U5 connected to AN 2, 3, 4, and 5, and given the physical positioning of the ADS7824P, it looks like those are the Analog Input pins.

Similarly, the documentation says that there are "2 DAC output channels A and B", and we see two pins connected to J3/U3, and physically proximate AD7237.  As such, it seems AN 8 and 7 are the Analog Output pins.

Then, recall the test above:

Quick test of the analog input-output
Connect AN connector pins 2 and 8 (DACA to ADC channel 0)
 

Enter
0 DACA 0 ADC . - answer should be roughly zero
1000 DACA 0 ADC . – answer should be roughly 1000
etc.
  

What they're really saying is:

AN 2 = ADC channel 0

AN 3 probably is ADC channel 1

AN 4 probably is ADC channel 2

AN 5 probably is ADC channel 3

AN 8 is DACA

AN 7 is probably DACB

If I got that right, then I think the documentation makes things a little confusing, only because the order of ADC vs DAC is reversed (they say pins 2 and 8 (DACA to ADC channel 0) where I think it would be better written as pins 8 and 2 (DACA to ADC channel 0).

Reference ground is available on a number of pins, but the proximate ones are:

ADC (J2, U4, U5): AN 2,3,4,5 have ground nearby (and represented in the connector) as  AN 14, 15, 16, 17, 18 (nearest pins on the wide side, adjacent to 2, 3, 4, 5).  But, you could also use AN 1 or AN 6 on the narrower side of the D sub 25.

DAC (J3, U3): AN 7,8 have ground nearby (and represented in the connector) as AN 19, 20, 21.  But, you could also use AN 9 or AN 10 on the narrower side of the D sub 25.

What is the purpose of the three LF412CN devices?

It seems the LF412CN op amp U3 is there for the purpose of amplifying DAC output to the pins to a particular voltage level.  Device pins 1 and 7 (OUTPUT A and OUTPUT B) find their way out to AN port pins.  

It seems, also, that the LF412CN op amps U4 and U5 are there to reverse-amplify the ADC signals before they're provided to the ADS7824P.  I suppose they also thereby provide some level of safety to the ADS7824P.  If anything's going to blow up, it's the socketed U4 or U5 chip, not the ADS7824P.

Existing AN wired-up connector

The controller came with a Dsub25 male connector that had three wires attached.  Orange = pin 2, unlabeled.  Two gray wires = pin 17 and pin 18, which we know now are GND.

As such, that connector could have been used for ADC channel 0 testing, but safe voltage is not yet certain. 

I re-wired it in order to be able to test against pins 2, 3, 4, 5 (input ADC), and 7, 8 (output DAC). 

Voltage testing (DAC outputs)

Wire AN 7 and 8 for testing V+ output.  Wire to correlated ground pins AN 19, 20, or 21.  Shrink-wrap for safe insulation.

Connect to multimeter.  Insulate/protect any unused wires for safety.

Issue values for the range 0..1000 DACA or 0..1000 DACB and report voltage results.

Test 1: AN 8 and GND

startup: 10v!

0 DACA = -10v

2048 DACA = 0V

4095 DACA = 10V

4096 DACA = -10V AGAIN!

3076 DACA = 5.03V

value is not affected by DE-ENERGIZE

Value is not properly controlled in a non-START condition.

> connect

> ROBOFORTH

> 3076 DACA = -9V

> START

> 3076 DACA = 5V

Connected to pin 7, DACB has similar controls, but what I'm seeing is a significant delay as the DAC output transitions.

First, on pin 7, at startup the pin is giving me around 4.37V, whereas DACA initialized at 10V.

> connect

> ROBOFORTH
> START

still at 4.37v

2048 DACB

0v

4095 DACB

10v

But then the next thing I'm observing is that the transition between values causes the DAC to dip to 0v before going to the new value.  

3076 DACB

Multimeter shows 0v for a little bit, and then 5v.

4095 DACA

4095 DACB

AN analog output pins are steady voltage, not PWM

 The following are pictures of scope traces from the Analog output set at 5v divisions with 0v at the midline.  These were formed by issuing commands like '0 DACB' or '1024 DACB' to the controller.

These findings DO NOT align with what is shown in tutorials5-axis.pdf, where it says, "0 DACA sends 0 volts out of DAC channel A".  What I found was that a value of 0 yields -10V relative to the Analog ground pin.

The only thing I'm not sure of is whether the AN ground pins are isolated and/or separate for the Analog output pins.


 

 


 


 

So... in the end, good news and bad news (maybe?) on the DAC front.

Good news

  • I'm pretty sure of my pin analysis.  DACA is AN 8, and DACB is AN 7.
  • Voltage can swing from -10V to 10V.
  • Both DACA and DACB lines appear to be working, though the initial voltage is odd.  (Would a 1 Meg or 100K resistor to GND help to bias it to 0v?  It seems like a solid voltage, like it's not floating, like maybe the controller just hasn't initialized a value.  But it is an amplified signal, so maybe it is floating behind the op amp?) 

Bad news

  • Initial voltage is strange and inconsistent.  Anything you connect should be prepared to receive +5 to +10 VDC at startup, prior to connection and until the first DACA or DACB command is issued.
  • Voltage can swing negative, so any circuit attached to the lines should have appropriate rectifying / guards. 

ADC inputs

Now that I know what DACA and DACB can emit, I can feel comfortable tying DACA to ADC 0, 1, 2, or 3. 

To test the inputs, I'll connect DACA (pin AN 8) to one of the inputs, starting with ADC 0 (AN2) and going up to ADC 3 (AN5).

Given the negative voltage that I'm not really expecting, and the initial value of 10v, I'll do this:

> connect
> ROBOFORTH

> START

> 2048 DACA

> 0 ADC .

 and then vary the values to DACA

Indeed, all of that worked.  ADC 0 reported back values that were typically 3 lower than what was sent in from DACA, even when ADC 0 was set to a 0 (-10v).

Repeat with ADC 1 (AN 3).  Same.

Repeat with ADC 2 (AN 4).  Same.

Repeat with ADC 3 (AN 5).  Same.



Analog - end result and findings

The Analog port AN is a Dsub25 female port.  A diagram of the pin numbers is shown earlier in this document.

It is connected through HE connectors to ports J2 and J3 on the MultiIOcard.  (That is an optional card, so this information may not be pertinent for all K11R controller owners.) 

DAC (output PWM signal) 

The DAC output ports are available on AN pins  8 (DACA) and 7 (DACB) with corresponding ground on neighboring pins AN 19, 20, or 21.  (Other ground pins on the Dsub 25 exist, but these seem like good choices due to proximity and header connections.)

DACA initializes at 10v

DACB initializes at around 4.4V

Neither gets a new voltage unless you issue the n DACA or n DACB command.

The value of n DACA or n DACB must be in the range 0..4095.  0 yields approximately -10v.  2048 is around 0v.  3072 is around 5v.  4095 is around +10v.

If n exceeds 4095, it loops back around to the negative values, so the controller is only paying attention to the lowest 12 bits of the 16-bit value in DACA or DACB.

Observed on an oscilloscope, the signal from DACB was a steady positive or negative voltage, not a PWM signal.  It's safe to assume DACA behaves the same way. 

Input ADC pins 

ADC ports are available at AN pins 2, 3, 4, 5.  The ADC command, given a channel number parameter, reads the DAC value from the device and puts it on the FORTH stack.  The pin and ADC channel print command relationships are:

AN 2 = 0 ADC .

AN 3 = 1 ADC .

AN 4 = 2 ADC .

AN 5 = 3 ADC .

Recommended corresponding ADC GND pins are  AN 14, 15, 16, 17, or 18.  (Other ground pins on the Dsub 25 exist, but these seem like good choices due to proximity and header connections.)

All four ADC pins had no problem reading back values in the 0..4095 from DACA.  As such, the pins are capable of receiving -10v to +10v, relative to GND.  It's not clear what would happen if an externally generated voltage outside of the -10v to +10v range were to be sent in to an ADC pin.  Hopefully, something like +12v would not generate a number "larger" than 4095, and thus loop around numerically (e.g., generate 4915 or hex 0x1333, whose low 12 bits would be 0x0333 instead of maxing out at 0x07ff).

DAC and ADC

Behind the scenes, the DAC and ADC pins connect to an op amp LF412CN before going to an ADC device (ADS7824P) or DAC device (AD7237).  

If the LF412CN chips blow up, they are socketed and are therefore replaceable.  Replacements currently cost about $2, ignoring shipping/handling.

Recommendations

If the AN pins are going to be used for any active mechanism (e.g., an end-effector), we should add a little bit more safety, because the AN pins are not set up with a consistent default voltage at power-up.

It might good to include an "enable" mechanism in the end-effector circuit.  That then would be controlled by a QA or QC output pin.  The startup sequence would be PROGPIA, 0 QA OUT, 0 QC OUT, 2048 DACA, 2048 DACB to get things in proper order.  After that's been established, the program can enable the circuit using whichever QA or QC pin was chosen, and then set DACA or DACB values as desired.

I'm not sure what to do about the voltage drop problem which I observed on DACB.  If, for example, a grip mechanism were to be holding an object with DACB at 4095, and then if we were to lessen the grip to DACB at 4000, we don't want the gripper to release completely.  But, it looks like that might happen, given the momentary voltage drop to 0v.  It may be a good idea to use a capacitor on the DAC output to avoid sudden changes.  It probably would be a good idea to try to capture that voltage drop on an oscilloscope to understand it better (particularly, measure its duration). 

Next steps

Consider looking at AN output pins on an oscilloscope both to understand voltage and PWM, but also to record and measure the perceived voltage drop.

Wednesday, March 11, 2026

ST-Robotics R12 arm - Gecko drives and max current draw

 I got a little more insight into the current draw for the motors.

In the CD of documents provided with the robot, there is a sub-manual for the Gecko drives.  It's in Other / G203V-REV-7-MANUAL.pdf (Rev 7, January 7, 2010).

That manual says...

The G203V will accommodate motor winding currents from 0 to 7A. Use the following equation to calculate the value, (in kilo-Ohms) of the current set resistor: 

R (in kilo-ohms) = 47 * I / (7 – I) 

Use the nearest standard value 5% tolerance, 1/4W resistor for this setting.

Here are the current set resistor values for motor current in .5A increments. Round the appropriate answer to the nearest 5% resistor value.
a. 1A – 7.8K
b. 1.5A – 12.8K
c. 2A – 18.8K
d. 2.5A – 26.1K
e. 3A – 35.25K
f. 3.5A – 47K
g. 4A – 62.67K
h. 4.5A – 84.6K
i. 5A – 117.5K
j. 5.5A – 172.33K
k. 6A – 282K
l. 6.5A – 611K
m. 7A – OPEN

 Separately, there is a table on page 20 of the R12 manual (PDF manuals / R12 manual.pdf) that lays out the resistor settings and amperage for the joints.

1 WAIST 3.0 36K
2 SHOULDER 3.5 47K 
3 ELBOW 3.5 47K
4 L-HAND 2.0 18K
5 WRIST or R-HAND 1.0 8.2K
6 WRIST 5 1.2 12K

Most of these amp values match that Gecko documentation table.

Does that mean the overall current draw is the sum of the amperages of the motors, plus some for the controller?  Maybe.

Even when a stepper motor is idle, it draws current as long as the windings are powered.  The Gecko specifications also say "Auto Current Reduction: 71% of set current, 1 second after last Step Pulse", but if all motors are pulsing in a given move, it seems like it could be drawing max current during that time and until one second beyond.

If that's the case, then even for my robotic arm that is lacking a 6th joint, I'll up needing a power supply rated for 13+ Amps (!).


ST-Robotics R12 robot arm and controller - ROS and Python

One path I'm exploring with the K12 arm and its K11R controller is whether I can make the robot operate with ROS.

I'm completely new to the ROS world of Robotics.  This puts me at least 8 years behind the learning curve.  And as such, I might not explain things really accurately here.

ROS 

The initial idea is that ROS is a bunch of middleware software that provides a common way for things to talk to each other.

There are "nodes", which conceptually could be things that have information.  In our world, a joint could be a node.  A set of configuration information could also be a node. 

Then, you have publish-subscribe mechanisms.  As an example, the robot arm could have joint location information.  You could have something that publishes the arm's joint location on a regular basis.  Then, you could have a visualizer that reads the joint information and renders it graphically.

There are also ways in which you can send actions, and so on.

This all came about with ROS way back in history.  Since 2018, ROS has moved on to ROS2, and the underlying APIs for coding nodes and actions, and the ways in which things are launched, has changed.

ST-Robotics has published a git repo at https://github.com/st-robotics that provides software for ROS, but it's for ROS v1 and hasn't been updated to V2 yet.  In fact, the last commit I could find was from September, 2018, and there hasn't been anything there since.

Nonetheless, the repository provides some insight into their intent.

ROS environment

In order to run ROS, I installed Ubuntu 24.04.4_LTS.  I could grab the ros package as part of Ubuntu, but found that the ST-Robotics code was built based on the ROS melodic version.

To keep things orderly, I set up a Docker image, and then set up ROS melodic within that.

The next thing I ran into is that ROS provides a user interface in MoveIt and Rviz, but you have to do some extra steps in order for the outer system's X display to be accessible from the inner docker instance.  

Then, I ran into issues with Python.  Part of the implementation in the ST-Robotics code is using a version of python and its dependencies that are quite outdated.  These days, too, it's recommended to run a virtual environment in python so as to avoid perturbing the global python libraries that everyone shares.  This is done for proper security reasons.

Overall, I have gotten to the point where I have ROS set up, but I don't really know how to launch the right pieces in order for a configured arm to appear in Moveit and Rviz.

However, I found some useful bits in the python shell and in the r12_hardware_interface code.

The python shell

Within the git repo, there is a section call r12_python_shell.

This originated as a shell built by Adam Heins.

The way it works is that you can run the r12_python_shell/r12/r12-shell script as an executable within Linux.  This depends on its shell.py, and arm.py.

shell.py is a subclass of the cmd.Cmd object, which basically provides you with terminal emulator behavior.  Text comes in to its reader, and the words get compared against known values in self.commands.  The parent object (cmd.Cmd) looks at strings that match commands, and redirects to functions of the form do_name() as they're found.

If a match for a string is not found in the list of do_name methods of the object, the default() method is called.

In this way, shell.py defines its own special set of commands (e.g., "connect"), and passes through other commands directly to the controller.  (BTW, from a naming standpoint, I think "arm.py" could be "controller.py" so as to distinguish between a K11R controller, and the device it's controlling.)

Before it can do any work with the controller, though, the system has to connect via the USB-to-Serial interface.  This is handled in arm.py in its search_for_port() method.  It scans the known USB devices for one that is from vendor 0x0403 and has the product ID 0x6001.

In Linux, you can see this kind of information by using the lsusb command and other similar commands.  However, you may need to be provided access in order for that to work.  First, the outer shell has to start docker in a particular way in order for the docker instance to see the usb devices.  For me, they're found at /dev/ttyusb0.  Second, even if the docker instance can see the usb device, the user that you're logged in as, when in the docker instance, has to belong to the right permission group.

Once set up, a typical sequence that I run is this:

1.  Start a terminal window in Linux

2. Start a docker instance, allowing the created docker instance access to the outer X display, and access to the usb devices.

$ cd

$ cd r12_python_shell

$ cd r12

$ ./r12-shell

This provides me with the cmd.Cmd prompt and awaits instructions.  The first thing to do is connect to the controller

> connect

And then, typically, I'll start ROBOFORTH, get the system fired up, and get the arm calibrated.

> ROBOFORTH

> START

> CALIBRATE

Modifications to the shell

I haven't diffed the original Adam Heins code against the version in the r12_python_shell area.  But I did try various operations and ran into strange delays at points.

One of the main problems was in getting the Teach pad to work.  After calibrating, I would run

> TEACH

and then it would sit there for a long time, and come back with an odd "Speed? <n>" message.

If I would run

> TEACH

again, then it would turn the lights on on the Teach pad, and I would be able to move the arm in joint mode.

The cause of the delay is that the basic python shell sets up a 30 second serial read timeout.  This is defined in arm.py.  It's there because they wanted to allow the arm enough time to run a calibration sequence without timing out.

The reason this gets in the way of the TEACH operation is that TEACH actually immediately responds with a request for a motor movement speed setting.  Normally, if you run this, the controller will respond as follows:

> TEACH

Speed? <16>

and then it waits for more input.  But the response isn't of a form that the shell was expecting, so it sits there waiting for more.  I think the second TEACH command I was sending was not being interpreted as an actual command, but as a response to the "Speed?" question.

The way I've gotten around the delay is to make the timeout amount much smaller (2 seconds), generally.  If the command is CALIBRATE, it adjusts the timeout (for that call only) to the longer 30 seconds.

The next problem is that normally you should be able to run TEACH, get the Speed? <value> response, and just hit Enter to accept the default value.  This is the reason that some of the documentation says "Type TEACH and hit Enter twice".  What they're really saying is "Enter the TEACH command, and then accept the default speed setting."

The problem in the Python shell is that the default cmd.Cmd code treats an empty line as "repeat the last operation".  Not wanting that, they overrode the command in shell.py's emptyline() method to just "pass", meaning it will not do anything.

Instead, we want an empty line to pass the empty line down to the controller.  I changed the emptyline() method to just call self.default("").

That leads to a third problem.  In its current form, if the default() method is given an empty string, it will not pass it along to the controller.  There are a few lines at the start that check for all uppercase in the string.  If it fails the check, it returns immediately.  It is a faulty assumption that everything going to the controller is uppercase.  In the case of TEACH, the response to the command is a numeric speed, or an empty line.  So, I've disabled the uppercase "Unrecognized command" check.

With that in place, it now works to enter Teach mode (equivalent of the T icon in Robwin), move the arm around in Joint mode, and hit the check mark on the Teach pad (or in British English, the "tick") to exit teach mode.  Then, I can run WHERE, or COMPUTE and POINT name, to capture the joint positions.

An interesting side-effect of this change is that lowercase commands are flowing through to the controller now.  But, they get uppercased somewhere in the path, so they still work.  "energise" is treated as "ENERGISE", and so on.  The only thing that doesn't make it through to default() is a command that matches the special python shell set of commands, like "connect" or "exit".  I've also found, interestingly, that uppercase "EXIT" behaves differently than lowercase "exit".

Cartesian mode with the Teach pad

I have not yet figured out how to get Cartesian movements of the Teach pad to work in the python shell.  They always end up giving me an error.  I think I have to observe what's going on with Robwin so I can know what has to be entered in the python shell.

Python shell vs. r12_hardware_interface

With just enough knowledge of the python shell's shell.py and arm.py code, it became clear that the same code is being used (with slight changes, probably) in the r12_hardware_interface arm.py code.

The r12_hardware_interface is a "ROS package providing a FollowJointTrajectoryAction interface to the ST Robotics r12 arm. Requires r12_moveit_config and my_r12_description packages. Based on Adam Heins' python shell for the r12." 

It has two ROS things going on.  

One is the joint_trajectory_action_server.py.  I don't fully understand it yet, but I'm starting to get it.

The _execute_cb() is a callback that is provided as a parameter to the SimpleActionServer constructor at startup.  Presumably, someone calls this to say "you, arm, go here!".  Within the _execute_cb(), you can see that it sends FORTH commands like "FIND TRAJECTORY" to find the route named TRAJECTORY.  It forgets it if it already existed (releases the variable and reserved memory).  Then, it creates it anew by saying "ROUTE TRAJECTORY" (create a route of the name TRAJECTORY), and follows that up with "500 RESERVE" (allocates space for 500 movement positions in the route).

Then, it converts the provided set of coordinates to joint positions, sends them to the controller using a special $JL command, and moves the arm along the route by issuing a special $RUN command.

After the route execution has begun, it loops and calls _update_feedback().  That in turn calls arm.read_pos().  That calls another special ROBOFORTH command, $, which gets the 5 joint positions back as an array.  (The serial response buffer may also have 'SPEED = nnnn' in the way, generated by the $RUN command, so the code has to ignore that on every arm.read_pos() call.)  

NB: I think the code that parses the joint positions could be improved with a regular expression parser.

The action server code continues to loop until the arm position indicates that it's close enough to the intended movement goal.

Separately, the arm position is also being published via the joint_state_remapper.py().  I think this allows some other ROS node to subscribe to the arm position and update its understanding of the arm's position.  I think this allows other subscribers (e.g., a UI visualizer like RViz) to see what the arm is doing. 

$JL, $RUN, and $ commands 

One big roadblock I face is that my controller does not understand the $, $JL, or $RUN commands.  Without these, the current ROS code won't work, and I can't consider upgrading the ROS v1 code to be based on ROS2.  It's like a fundamental dependency isn't being met.

The $JL command lets this external system (MoveIt, or any other caller from Python land) construct a set of ROUTE points without actually moving the robot arm.  Recall that in the originally intended operation from ST-Robotics, a user would move the joints with the Teach pad, or lead the arm "by the nose", and capture joint locations.  Those locations would be stored as positions within a ROUTE.

As it stands, it is not clear where ROUTE memory is stored.  It's also not obvious if there's some other way to hack values into a ROUTE without actually moving the arm.  The only way I know to add a point to a route is by using the LEARN operation, and that pulls the joint positions from the encoders.  $JL is like a "poke" or "store" operation that allows programmatic assignment of the route position without reading the encoders.

The $RUN command appears to be similar to a normal RUN operation.  But, perhaps it's non-blocking.  I'm not sure.  I think ideally it returns immediately (whereas I think RUN is a blocking call) and that allows the rest of the system to continue, checking for positional changes until complete or interrupted.

The $ command is terse, and provides a way to get arm joint position information while the $RUN is in process.  Its output differs from WHERE in few ways.  First, it seems to always report position information in joint form, i.e., as motor step positions.  It does not report data in Cartesian form.  Second, it's terse.  The command is a single character, reducing transmission speed, and the response is just a set of five integers.  By comparison, the WHERE command returns a header line and three lines of data, and it may return Cartesian values if you don't ensure you're in JOINT mode first.

The github repository states that the $JL and $RUN commands are required, and if you can't run them, you should contact ST-Robotics support.  I've tried that, and have not heard back.

Other findings

I'm a little confused why the python shell (and subsequently the arm.py code in r12_hardware_interface) establishes serial communication with two stop bits.   Other parts of the documentation mention how you can change the baud rate, and I believe it says to use one stop bit.   The existing shell code works with two bits, so I'm not wholly inclined to try to change that to one.  But it may be an opportunity for a very small speed-up.  I think it may be doable to improve overall communication speed by just bumping up the baud rate.  That might not work in high (electrical) noise environments.

One neat thing I found in r12_hardware_interface  / joint_state_remapper.py is conversion ratios.  There is this chunk of code:

RATIOS = [3640, 8400, 6000, 4000, 4500]  #Joint motor counts corresponding to 90 degree rotation

This defines the number of steps required on each axis in order to move 90 degrees.  While it's not clearly commented in the code, the order of values is waist, shoulder, elbow, hand, and wrist.

So, you can say

> connect

> ROBOFORTH

> START

> CALIBRATE

> TELL WAIST

> 1820 MOVE

> TELL ELBOW

> 6000 MOVE

and you should have the waist rotated 45 degrees (half of 3640) and the elbow-to-hand section level to the floor.

These values will be important if I'm to do any of my own route computations.

 

 

ST-Robotics R12 robot arm with K11R controller

 We've had the good fortune of coming into possession of a 5-axis robotic arm.  It's a K12, made by ST-Robotics in 2018.

The machine is mostly as depicted in other web pages.  The arm looks as you'd expect a red and white robotic arm to look like.  It does not have working LED lights.  

The controller is a K11R.  It is a rather industrial-looking thing, boxy with the appearance of being rack-mountable.  There is a control pad for "teaching" the robot.

Power

The first challenge with the robot controller is that it did not come with an obvious power plug.  In most other pages that show an R12, you see a typical 110V female port on the back.  (I supposed a European model might have a 220V plug.)  Instead, for this one, there was a circular plug, and a locking connector with two pins connecting to external red and black wires.  There was no documentation about intended voltage or current draw.

These days, for any device these days that has some kind of power plug, you will normally see a label, declaring the power type -- voltage, AC vs DC, polarity, and amperage.

I disassembled the controller to determine the voltage.  The inner workings of the controller are pretty spaciously housed, but not clearly labeled.  There was no transformer.

After getting pretty deep into the controller, I found that the red line went through a fuse, and then branched outward from terminal blocks.  Some of the branches connected to the stepper motor power port of Gecko motor drivers.

I then took a peek under the labels that covered the original labels of the arm motors.  After some searching, I found that the typical voltage to drive them would be 24 VDC.  So, I was pretty confident that it would work if I used 24 VDC on red, and 0 VDC on black. 

The documentation (page 9, see below under Documentation and software) states that the power connector has a "6.5A anti-surge" fuse for 110v power and says there's an equivalent 3.5A anti-surge for 220-240v power.   

After adding a bench power supply, pre-set to and tested at 24 V, I hooked up the controller and arm, and got a good "motor powered" single knock sound from the arm.

Subsequently, I've changed things to use a spare 24v, 10A dedicated power supply.  I'm pretty sure I pulled that from a Cube Pro Duo hefty 3d printer.  

Documentation and software

The arm came with a Mini CD.  On it, there is one directory, called manuals.  In that folder you can find many things, including PDF manuals, software, and drivers.

Manuals

There are lots of files provided in the manuals/manualsPDF folder on the CD.   Several documents on the CD are relevant for my set-up, but most are not.  Some describe other arms (e.g., R17) or expansion cards or other hardware that I do not have (e.g., teaching pads, end-effectors). 

Many of the manuals on the CD are not posted online.

Some manuals exist online.  The R12 manual is most commonly found, e.g., https://docs.rs-online.com/d934/0900766b8152ed18.pdf 

Some information I found online was not on the CD.  An important one is the useful tutorial7.pdf.  On subsequent review, it looks like this is a revision of the tutorial5-axis.pdf document in the Manuals/PDF manuals folder on the CD.

The ST-Robotics github page refers to documentation, but the links point to a now-defunct section of the ST-Robotics web site, and thus yield a 404 error.

Software 

Robwin provides the main UI for interfacing with the arm.  There are other command-line ways to do so, too, but unfortunately not all Robwin functions are described in command-line equivalents. 

The github repo provides Python code and ROS (v1)-related code for interfacing to MoveIt and Rviz.  The "python shell" provided at the git repo originally came from the Adam Heins implementation at https://github.com/adamheins/r12

Videos 

There are also several videos available on Youtube from ST-Robotics.  It appears that they are hosted by David N. Sands himself.  

Some of the same content is found in .mp4 form on the CD.

ST-Robotics had one more video posted to Youtube, showing usage of an R12 with ROS (v1). 

Recommendations

I have found these resources to be the most helpful:

1. The R12 manual

2. The tutorial7.pdf pages linked from superdroidrobots (https://www.superdroidrobots.com/blog-a/manuals/tutorial7.pdf)  This may be the same as or an earlier revision of tutorial5-axis.pdf.

3. The MultiIOcard.pdf manual.  This is only on the CD and not posted anywhere.  It describes the expansion ports and card and connections.  This is only relevant if your K11R is configured to include this card.

4. The source code and utilities at the Github repo (https://github.com/st-robotics)

5. The D.N.Sands Youtube videos.

Physical connections

The controller connects to the arm at two connection points, each of which is clearly labeled on the back of the controller.  One of these is referred to in the doc (section 12, Connections, page 25) as "25-way D-type, female", or what I'd call a D Sub 25 female port.  The other is a circular connector with lots of holes and a locking connector.

A front-side RS-232 serial port, labeled COMPUTER, connects to my laptop using a USB-to-Serial connector.  An FTDI driver disk was provided with the machine, but my Windows 7 laptop already had the drivers installed.  

A 9-pin female port labeled TEACH on the front connects the Teach pad.  The protocol for communication between the Teach pad and the controller is not described.  

Most ports are labeled clearly, and really don't give me any opportunity to mess up the connections.

OUTPUT (PA) and INPUT (PB) ports 

The OUTPUT port on the back is a 15-pin D Sub female.  Below it is an INPUT port, which is a 9-pin male port.  It turns out that these are ports PA (OUTPUT) and PB (INPUT).  Connections to these are optional.

On my controller, these were only labeled OUTPUT and INPUT.  The relationship to PA and PB could only be determined by shape, inspection, and testing. 

Expansion ports 

There are three more 25-pin ports on the back, and with a modified panel plate, there would be room for one more.  After much analysis, I've found that the ones I have connect to the J4 and J5 points on a MultiIOcard inside, and the other one appears to be for analog input/output.  As with PA and PB, connections to these are optional.

STOP jack 

Finally, while there is a STOP button on the front control panel, there is also a 1/4" jack on the back of the box for a second STOP switch.  Mine has a 1/4" plug that has two normally closed, temporary switches wire in series between the barrel and tip. 

The simple presence of an empty 1/4" plug in the jack does not allow for normal operation.

Without this STOP switch/plug in place and in a closed circuit state, the controller will power on, but will not respond to arm movements (e.g., will not honor a CALIBRATE command). 

Robwin software set-up (Windows)

The initial foray into working with the robot is to use the provided Robwin.exe program.  That runs on Windows, and provides a graphical user interface.  Among its features, the UI allows you to type and send FORTH commands, kind of like you have a terminal emulator for FORTH.

There is a .msi installer provided on the disk for installing Robwin.

For modern computers, connection to the RS232 serial port is done by a USB-to-Serial connector.  If necessary, an FTDI driver must be installed.  (It's available on a disk provided with the machine, and also can be found online.)   

Even with the driver installed, Robwin will not recognize the controller if it is connected to a COM port numbered higher than 9.  It's not clear why Robwin faces this limitation.  It turns out that upon connection, my laptop-to-controller connection was on COM16.

As it turns out, there are ways to remap the COM ports on Windows to bring a given COM port down to the desired range, as long as all those aren't already taken.  For me, they were already taken.  I disabled other software that was hogging the lower COM ports.  Between that and the remapping, I was able to get Robwin to see the controller.

Initial operation 

Much of the robot operation is as described in the demonstration videos and documentation.

The initial discussion of the machine centers around its use of FORTH as a base language.  I think I read somewhere that it's running a Z80 processor.

The main drivers are Geckos, one for each joint.  

The system is described as having an outer interpreter, and an inner interpreter.  I don't really care which is which.  The thing is, when you connect to the machine, you're using a plain FORTH interpreter.  Then, within that you issue the command ROBOFORTH, and that starts up another FORTH interpreter that has knowledge of all the special commands that let you control the robot.

You connect to the controller, run ROBOFORTH, and then say START.  At that point, if everything has gone well and all the connection checks are ok, the joint positions are assumed to be at 0, 0, 0, 0, 0, wherever the arm is currently positioned.  But, normally (and especially when first learning), you'll want that home position to be a known location, so that's why you run a calibration sequence.

To calibrate, you do this:

  • De-energize the motors (run DE-ENERGISE or DE-ENERGIZE)
  • Manually rotate the waist and arm joints roughly to a position where it's pointing straight up
  • Restore power (ENERGISE or ENERGIZE)
  • Run the CALIBRATE command.  

The CALIBRATE command moves the joints slowly to known points.  As each joint's calibration sensor point is found, it moves on to the next joint.  When all joints are calibrated, it rapidly moves all joints, leaving the arm in a fully up position.

If the CALIBRATE operation moves a joint a certain number of stepper motor steps (or perhaps a pre-defined amount of time), and does not hit its expected calibration sensor, the CALIBRATION operation stops and gives you an error message.  This may happen if a joint wasn't close enough to the sensor position to start with.  In that case, you might just want to run CALIBRATE again right away, and let it continue to the desired point.  However, if a joint is moving significantly away from the upright position, you might be better off de-energizing, manually moving the arm more upright, and trying anew.

The bottom of the arm doesn't really indicate where the "front" is.  You kind of have to guess and experiment, and be ready to hit the STOP button during movements until you're comfortable with its position.

Upon successful calibration, the arm joint positions will be at 0, 0, 0, 0, 0, which is its knowledge of "home".  You can see the step position by running the WHERE command.  If you were in Cartesian mode, you may need to run JOINT and then WHERE.  If you operate in Cartesian coordinates, you will see that the home position is X=0, Y=0, Z=5000 (i.e., 500.0 mm). 

The documentation provides other ways to modify the zero calibration points so that the arm isn't sticking straight up initially.  This can be important for environments that have limited vertical clearance, for example. 

Joint movement

For initial operation of the robot arm, you can enter either Joint teaching mode or Cartesian teaching movement in Robwin.

Clicking the T icon starts movement in joint mode.  Clicking the L-shaped axes icon starts Cartesian teaching mode.  The teaching pad becomes active, and you can move the robot arm joints by clicking on a joint button first, and then + or - to move.  

When in Joint mode, a joint moves continuously as long as the + or - button remains pressed.  The speed button, followed by + or -, changes the rate of joint movement.

In Cartesian modem, movement is stepwise, and the meaning of "speed" changes to mean an axis movement distance.  Lower step sizes might be 1 mm, whereas higher step sizes might be 10mm, or more.

What is joint movement?

The joint movement done by the teaching pad is a reflection of the relative MOVE operations underlying the system.  The controller moves the joint stepper motors by a number of steps, managing acceleration and jerk to reach the destination smoothly and with precision.  (And when I say "smooth movement", that's different from the smoothing function that the controller provides for route travel, explained later on.)

The waist joint rotates the arm around its vertical axis.

When the shoulder moves, everything further down -- the elbow, hand, and wrist -- comes along for the ride.  Similar applies to elbow movement.

Focusing on the elbow, it's important to realize that when it moves, the hand orientation relative to the elbow remains the same.  For example, suppose you calibrate, and the arm is pointing straight up and the hand is also pointing straight up.  If you rotate the elbow 45 degrees, the elbow-to-hand orientation is still straight.  But relative to ground, the hand is now 45 degrees.

The hand and wrist are a bit odd.  When the hand joint moves, the wrist does, too.  That's a result of how the gearing is done to control the wrist.  

What is Cartesian movement?

The controller provides a mechanism for doing "Cartesian" movement.  You can think of this as moving the hand-wrist in an X, Y, Z coordinate space.  At the calibrated home, you're at (x,y,z) = (0,0,5000), where 5000 means "500.0 mm".

When the Teach box is used to move in the Cartesian system, the top, left 3 buttons are for controlling X, Y, and Z motion.

Suppose you want to move the hand/wrist outward along the positive X axis.  You click the X button on the pad, and then hit +.   (The box's "speed" setting controls how far it moves along the axis.)  By moving out along X, the robot cannot possibly extend itself to maintain the original Z height.  So, the software computes new Y and Z locations that will accommodate the intended X position.  It moves all joints as needed to achieve the new X position goal.

Cartesian movements only cover X, Y, and Z positional changes.  As such, to reach a new location in one axis, the controller only has to figure out a "bend" in the other two.  The teach pad still lets you move the hand and wrist joints.  When in Cartesian mode, those are reported back as angular positions instead of motor step counts. 

More operations and details

There are mechanisms for setting up and running smoothed routes.  This is really the main function of the R12 robot.  It was built with the intent that users would move the arm to particular locations, save them as points in a route, and then run the route repeatedly.  
 
The smoothing of a route makes it such that as you move the joints from one point to another, it doesn't do a straight vector movements.  For example, suppose you have a route with four points 1, 2, 3, and 4.  You could have the arm move from 1 to 2, pause, 2 to 3, pause, and then 3 to 4.  That's what a route run looks like without smoothing.  With a smoothed route, it moves from 1 to 4, curving the motions around points 2 and 3.  It's not clear if the kinematics computations attempt to hit points 2 and 3, and maintain momentum, or if it moves near 2 and 3 as it eventually gets to the ultimate goal, point 4.
 

Addenda 

tutorial7 highlights

Some of the important things about the tutorial7 document

  • It seems to have come from ST Robotics originally.
  • Page 12 explains flag bytes that store motor enable and direction for each joint, e.g., 1 MEP C! 0 MDP C! 1000 MOVE for moving the waist 1000 steps.  MEP is preceded by a bitwise value (1 = waist, 2 = shoulder, 4 = elbow, etc.).  MDP is preceded by 0 for positive movement, or 2 for reverse movement.  (Does the existence of a per-joint MEP operation mean that you can selectively de-energize?)
  •  Page 15 shows how the command "TELL joint" is a separate operation from the MOVE or position MOVETO commands.  So, you can say TELL WAIST as one command, and then 1000 MOVE and -1000 MOVE as subsequent commands for relative movement.
  •  Page 19 shows how you can use the POINT name command to store a current location in a variable called name.  Later, you can use name GOTO to move the arm back to that point.  This is different from learned route points, but can be helpful in setting up a route.
  • Page 23 mentions the decimeters PLUNGE command.
  • Page 27 starts talking about "objects" and their presence markers at route locations.
  • Page 28 shows delay operations, e.g., MSECS or n USECS
  • Page 32 discusses some of the route location learning operations that can be performed.  However, it only goes into what the Teach pad is revealing.  What isn't shown are the route location reservation commands:
    • ROUTE name
    • n RESERVE
    • (use MOVE or MOVETO or GOTO commands to move arm)
    • LEARN
    • L.
  • Page 36 shows some commands that are not explained: LINE, EXAD, AXES, TRANSFORM.  It also discusses the CRUN and DSPASSUME commands.  I have not been able to try those yet, and don't really understand their purpose.
  • Page 36 also mentions the COMPUTE command.  This one is important, because it's pretty easy to move the arm using JOINT operations (number of motor steps a given joint moves), but in order to save those, you have to COMPUTE (get the controller to convert joint location information to Cartesian) before you can save the information as a point (using POINT name).
  • Page 39 shows how the L. command can show relative lines.  But, it doesn't really explain how a line is marked as being a relative movement.  This also appears on page 41 when discussing the "set approach" or "Set Apprch" button.
  • Page 50 discusses ENCASSUME which allows the system to pull the encoder locations as the true location.  (It's not clear what the "assume" word means here.  It could be a FORTHism, or it could mean "assume that the encoder locations are representative of a real location and are relatively accurate".  ENCASSUME is referenced later in a description of how you can do "lead by the nose" teaching, wherein you de-energize the motors, move the arm around, and then record a new position using ENCASSUME.  If you don't run ENCASSUME upon re-energizing, it will find a significant difference between the encoder positions and the last known instructed position, and will think that the arm crashed. It won't work well from then on.
  • Page 52 discusses the I/O ports PA and PB, but doesn't go into enough detail.  I'll have a separate page that discusses ports, pins, and bit offsets.  But, it does provide commands such as
    • PB IN .
    • PB 5 BIT? .
    • PB 5 1 WAIT
  • Page 52 also touches on usage of the ADC and DAC pins.  But, it does not really describe which pins of the 25-pin connector are used for what purpose.