[GreenKeys] BRPE Restoration

Eric Moore mooreericnyc at gmail.com
Sun Dec 31 12:26:29 EST 2023


Howdy! I dug up the updated code from Guy Fedorkow at MIT, and the notes he
sent.

There is also a better pic of the updated controller attached.

The magnets were always 24V 1A AFAIK. Regardless, it runs great at full
speed with the PSU and MOSFETs linked below.

-Eric

The code does a couple things
1.  It punches two fixed patterns.
    - There's a black push button; that punches a "one hot" code sequence
as long as it's held.  I used that to set the timing...  as long as the
line is sloped properly, each punch must be punching in turn.  To allow
tweaking the angular pickup without drowning in tape, this one punches very
slowly.
    - there's a toggle switch that punches a pseudo-random sequence, being
careful to always start from the same point.  With that, I could punch ten
or twenty feet of tape, and keep doing it until the result read back from
the tape lines up with the expected sequence.

2.  I put a hex serial downloader, using a simple syntax similar to what
you had...  I think it starts with two Z's, then a count, then hex-ascii
bytes.  I actually didn't use that much, since I got tired of trying to
figure out how to share the serial port between the IDE and some kind of
Linux serial loader.  So it's probably buggy.

3.   I ended up converting the patterns I wanted to punch into C
initialized arrays, and then just compiling them into the Arduino code.
That needed a teeny command line, which can be accessed through the Arduino
IDE serial console capability.  I removed most of the ones that were
specific to my Whirlwind work, but you can see a couple left in the code as
examples.
  I think lower-case L\n lists the names, then a number and carriage return
starts the punch on that pattern.

  I suppose if you are going to use the unit for demos and educational
purposes, an additional hack might be to accept a string from the command
line, punch it in block letters, then in ASCII codes, so people could get
their names punched into tape.

  Whirlwind used a bit-order opposite to what I think "modern" paper tape uses,
but I think all the swapping happened on my side.  So I think Bit Zero
punches as Bit Zero.  But don't be surprised if not :-(



  For hardware, I re-used your Freeduino board.

  The magnet drivers are Pololu Big Pushbutton Power Switch:
https://www.digikey.com/en/products/detail/pololu-corporation/2812/10450610?utm_adgroup=Development%20Boards%2C%20Kits%2C%20Programmers&utm_source=google&utm_medium=cpc&utm_campaign=Shopping_DK%2BSupplier_Pololu%20Corporation&utm_term=&utm_content=Development

  I used a power supply like this:
https://www.digikey.com/en/products/detail/400525-06-0/3243-400525-06-0-ND/13885812?utm_medium=email&utm_source=oce&utm_campaign=4251_OCE21RT&utm_content=productdetail_US&utm_cid=2227597&so=72620049&mkt_tok=MDI4LVNYSy01MDcAAAGAhuiWqA2C02mNfKO13Bmbir2lEuY7jDD7yAllEgJN90KDNJfkSoc22T_VvRViohOr3VeOwT5JJF3a1YYVeQ_FZXEbvTflV-J3jOsDexfA
  But it's open-frame, and obviously would need a box to be safe to use.
  I continued to just power the Arduino from the USB connection.

  I did notice that while the Arduino is reset, it seems to turn on four
punch drivers.  I didn't spend any time trying to figure out why, but it
would be good to not let it stay in that state for too long (minutes at a
stretch).  I think most of the power goes into the damping resistors on the
bottom of the chassis, but I don't think they're rated for that much power
continuously with additional cooling.

  If you examine the circuitry at bit, you'll see there's one unused output
and bipolar driver transistor...  I'd intended to use it to drive an LED
for an ad-hoc strobe, but I found it too hard to adjust the timing of
strobe and magnet independently.  YMMV.

On Sun, Dec 31, 2023, 10:55 AM <pbirkel at gmail.com> wrote:

> Eric;
>
>
>
> I was just finishing a message to you on this subject.  Had to dig a bit
> to figure out your email address!  Thanks for “piping up”!
>
>
>
> I was wondering on what basis you chose 28 Vdc to drive the control relays
> and the various timings implemented in your .ino file?  My BRPEB11 base
> includes an array of nine top-hat diodes and nine 25 ohm power
> (wire-wound?) resistors that suggested to me that probably 100 Vdc, as used
> for Teletype signaling, was intended.  Does your base also include these
> components?  Does it use the same 24-pin Centronics-style signal connector?
>
>
>
> Also, while I can see a bit of your wiring hook-up in the video (
> https://youtu.be/-aZl8rMT8Aw?t=129), I’d appreciate it if you could share
> a schematic along with information about conditioning the input from the
> “synchro-pulse magnet” as well as driving the relays (possibly this is a
> pair of ULN devices based on what little I can see in the video).
>
>
>
> Please do dig up the updated/final code!
>
>
>
> Thus far my primary reference documents have been “1154B_BRPE_Parts_Catalog_Jul1960.pdf”
> and “215B_BRPE_Technical_Manual_May1971.pdf”, neither of which is
> particularly satisfying regarding relay driving parameters and schematics
> :-<.  Any information that you could add to them would be greatly
> appreciated!
>
>
>
> Thank you.  Good Health to You and Yours,
>
> paul
>
>
>
> *From:* Eric Moore <mooreericnyc at gmail.com>
> *Sent:* Sunday, December 31, 2023 11:45 AM
> *To:* Paul Birkel <pbirkel at gmail.com>
> *Cc:* Greenkeys <greenkeys at mailman.qth.net>
> *Subject:* Re: [GreenKeys] BRPE Restoration
>
>
>
> https://github.com/emooreatx/Teletype-BRPE
>
>
>
> https://youtu.be/-aZl8rMT8Aw
>
>
>
> Here you are, code and video. I got the BRPE going full speed later.
>
>
>
> I will try and find the updated code from MIT I am now using. Not sure I
> ever updated github.
>
>
>
> -Eric
>
>
>
>
>
> On Sun, Dec 31, 2023, 6:03 AM <pbirkel at gmail.com> wrote:
>
> I was lucky enough(?) to acquire a BRPE21 on eBay.  Some pics at:
> https://www.ebay.com/itm/335180419867
>
>
>
> It arrived yesterday and “now comes the fun part” :->.  Overall it’s in
> pretty good shape, as seen in the pics, however two pins are stuck and some
> others are sticky.  Not too surprising but I’m enthusiastic about having to
> disassemble this unit down to the punch-block and then getting it back to
> operational order.  So I’m very interested in hearing from anyone
> maintaining a similar unit.  The vital information:
>
>
>
> Base unit is BRPEB11 (side plate by power switch).
>
> Punch unit is BRPE21 (8 level with three suppression switches).
>
> Motor unit is LMU6 (plate on motor mount).
>
> Motor plate states: 5PA66HV2A / Type: PA / V: 115 / CY: 60/50 / WD: SERIES
>
>
>
> Base unit includes 9x top-hat diodes and 9x 25 ohm power (wire-wound?)
> resistors.  Signal connector is a 24-pin CINCH 57-40240 female with cable
> retention clips.  Example: https://www.ebay.com/itm/394787953341  The
> connector is mounted in a wider cutout (but same end-to-end mounting
> points) so presumably there were alternative connectors in use.  Includes
> cable retention clips.  Example: https://www.ebay.com/itm/394787953341
> Power connector is a recessed socket sporting a center pin plus a pair of
> curved blades (one locking); I’ve never had any luck matching that style of
> socket connector so I guess that I’ll need to replace it, unless someone
> has the right plug, unused.
>
>
>
> This unit appears to be a modestly newer than those documented in
> 1154B_BRPE_Parts_Catalog_Jul1960.pdf (which appears to only document up to
> the BRPE11 punch and BRPEB8 base) although
> 215B_BRPE_Technical_Manual_May1971.pdf does seem to apply.
>
>
>
> In 1154B_BRPE_Parts_Catalog_Jun1966.pdf, page 32 (Figure 1) the population
> of the back-plate appears to correspond, so I think that PN #114466 may be
> the matching power plug (however that PN is subsequently described as
> “Connector, Receptacle (2 Pt)” rather than as the plug).  Note also in the
> figure the four rubber grommets (PN #154697) affixed to both sides of the
> base fore(ish) and aft.  What purpose are these intended to serve?
>
>
>
> Also in 1154B_BRPE_Parts_Catalog_Jun1966.pdf, page 33 (Figure 2) note the
> two-part metal tape reel (PN #146689 “Disk, W/Hub” & #146690 “Disk,
> W/Flange”) that is used to dispense tape.  I seek one of these, or
> guidance/experience in creating a substitute.
>
>
>
> Also in 1154B_BRPE_Parts_Catalog_Jun1966.pdf, page 35 (Figure 4) is
> illustrated the “motor cover” that I’m missing (PN #143092 or perhaps
> #142991).  Just askin’ …
>
>
>
> Finally in 1154B_BRPE_Parts_Catalog_Jun1966.pdf, page 44 (Figure 7) I’m
> missing the chad box (PN #142933; appears to be a simple metal bin but the
> HappyComputerGuy video on YouTube shows one in transparent plastic, which
> makes a bit more sense) and then on page 45 (Figure 8) the “Guide, Tape” PN
> #142983 has somehow been broken in half (looks like a lot of metal fatigue;
> strange).  At least these are parts that I can manufacture for myself …
> although an original chad box would be a lot nicer than anything that I’ll
> be able to produce.
>
>
>
> Aside from the “parts call”, above, my initial questions:
>
>
>
>    1. 295B-6504.pdf states that the LMU6 series (governed) motor is
>    AC/DC.  I infer that if I simply supply 120VAC 60Hz the governing
>    components/circuitry will have no adverse effect, but that’s not made clear
>    anywhere.  The curved-blade power socket seems to suggest that this
>    installation was intended for DC operation, so I’m reticent to simply
>    supply AC “and see what happens”.  (Shaft turns freely and depressing the
>    four spring-ball oiling ports appears to indicate the presence of
>    sufficient light-weight oil, so other than modest cleaning the motor
>    assembly appears to be ready-to-go.)  Figure 5 (page 16) in 295B-6504.pdf
>    is the applicable schematic; there’s no evidence of power supply
>    polarization.  Is this correct for DC operation (either orientation is
>    fine)?  Can I simply use AC without any changes?
>
>
>
>    1. 215B_BRPE_Technical_Manual_May1971.pdf has very little to say
>    regarding how the BRPE was driven (“deserialization" / "receiver set").
>    Sections 3.05 through 4.02 (page 21 of the PDF) provide a very generic
>    description of signaling, and then Figure 3 (page 22) documents a
>    conceptual diagram including a “SIGNAL REGISTER” with input “SYNCHRONIZING
>    PULSE SENT TO SIGNAL REGISTER” and output “FEED AND CODE PULSES SENT TO
>    MAGNETS”.  I’ve not found any schematics for the BRPE base, however as it
>    houses 9x diodes and power resisters I’m guessing that the code pulses are
>    “typical” 100Vdc’ish signals.  Is that correct?  Note that the
>    https://www.youtube.com/watch?v=-aZl8rMT8Aw video shows a 28Vdc power
>    supply present for some purpose; as it peaks at ~530 mA during operation I
>    presume that it’s the magnet supply.
>    https://github.com/emooreatx/Teletype-BRPE/ certainly draws that
>    relationship.
>
>
>
> Timing of the code vs. feed pulses isn’t stated anywhere, nor their
> durations.  Figure 2 (Page 20) is rather stylized and implies that the feed
> pulse is concurrent with the code pulses, which I guess based on initial
> study of the mechanics could very well be true. Based on the time-line
> across the top of Figure 3 it appears as if both code & feed pulses start
> with the synchronizing pulse at 90 degrees before TDC and end ~80 degrees
> after TDC, so a nominal duration of somewhat under a half-cycle.  It looks
> like the mechanism deals with offsetting the actual punch and advance
> actions.  (Yes the Arduino code in GitHub documents one timing approach;
> I’d like to determine if it’s the proper one …)
>
>
>
> The synchronization pulse is just the raw magnetic pickup, so the
> implementation of the “SIGNAL REGISTER” needs to both amplify/trigger and
> then create the correctly timed (and gated in the case of the code)
> pulses.  It would be quite helpful to see the schematic & technical
> description of the Teletype equipment that implemented this function!
>
>
>
> As my primary use-case is based on a TTL source it looks like I’ll need to
> not only handle the triggering and timing but also opto-isolation of 10x
> 100 Vdc signals for the various relays (or maybe that’s “only” 28Vdc).  Has
> anyone here been down this road, even part way, before me?
>
>
>
>    1. There really isn’t any documentation that I can find that addresses
>    punch disassembly, cleaning, and reassembly … although proper lubrication
>    is addressed.  I’m not enthusiastic about tackling the breakdown necessary
>    to properly service the punch block/pins lacking documentation.  I’m hoping
>    that I can extract a subassembly that I can simply immerse in solvent or
>    otherwise apply solvent to selected points while working individual pins to
>    get them moving freely, and then relubricate.  Unfortunately I don’t see
>    any way to accomplish this without a pretty complete disassembly :-<.
>    Suggestions?  I am assuming that a lightweight “sewing machine” oil will be
>    satisfactory when relubricating.  Is that correct?
>
>
>
> I do spy https://www.navy-radio.com/tty/brpe.htm so maybe Nick has
> answers (and more!) to all of my initial questions.
>
>
>
> Thoughts, observations, experience, etc. that have a bearing on my BRPE
> will be much appreciated.  And …
>
>
>
> An early Happy New Year!  One hopes that 2024 will be less “interesting
> times” than 2023.  But hope is not a method …
>
>
>
> paul
>
>
>
>
>
> ______________________________________________________________
> GreenKeys mailing list
> Home: http://mailman.qth.net/mailman/listinfo/greenkeys
> Help: http://mailman.qth.net/mmfaq.htm
> Post: mailto:GreenKeys at mailman.qth.net
>
> >>> Jordan Spencer Cunningham's GreenKeys Search Tool:
> https://teletype.net/gksearch
> >>> 2002-to-present greenkeys archive:
> http://mailman.qth.net/pipermail/greenkeys/
> >>> 1998-to-2001 greenkeys archive:
> http://mailman.qth.net/archive/greenkeys/greenkeys.html
> >>> Randy Guttery's 2001-to-2009 GreenKeys Search Tool:
> http://comcents.com/tty/greenkeyssearch.html
>
> This list hosted by: http://www.qsl.net
> Please help support this email list: http://www.qsl.net/donate.html
> Message delivered to mooreericnyc at gmail.com
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.qth.net/pipermail/greenkeys/attachments/20231231/fd0225ff/attachment-0001.html>
-------------- next part --------------


#define TRUE 1
#define FALSE 0

#define MAGNET_ACTIVATE_MS 2

uint8_t chatty = FALSE;

// #define LED_PIN 17           // HiLetGo "Leonardo" LED pin
#define LED_PIN LED_BUILTIN  // default for generic arduino
#define STROBE_LED_PIN     2
#define PUNCH_ADVANCE_PIN  3     // teletype pin 9 // magnet to punch a sprocket hole
#define PUNCH_TRACK_0_PIN  4     // teletype pin 8 // the eight magnets for eight tracks.  I'm assuming these will remain in sequential order
#define PUNCH_TRACK_1_PIN  5     // teletype pin 1
#define PUNCH_TRACK_2_PIN  6     // teletype pin 2
#define PUNCH_TRACK_3_PIN  7     // teletype pin 3
#define PUNCH_TRACK_4_PIN  8     // teletype pin 4
#define PUNCH_TRACK_5_PIN  9     // teletype pin 5
#define PUNCH_TRACK_6_PIN  10    // teletype pin 6
#define PUNCH_TRACK_7_PIN  11    // teletype pin 7

#define SYNC_PULSE_PIN 12  // one pulse per rotation
#define ONE_HOT_BUTTON_PIN A1  // pushbutton for starting test sequence
#define RAND_PUNCH_SW_PIN  A0  // switch for starting random punch sequence

#define DELAY_CYCLES 1  // Wait states between punch cycles.  Set this to zero to punch at full speed.

#define _PRINTF_BUFFER_LENGTH_  64
static char _pf_buffer_[_PRINTF_BUFFER_LENGTH_];
#define _Stream_Obj_        Serial
#define printf(a,...)                                                   \
  do{                                                                   \
    snprintf(_pf_buffer_, sizeof(_pf_buffer_), a, ##__VA_ARGS__);       \
    _Stream_Obj_.print(_pf_buffer_);                                    \
  }while(0)

const uint8_t tape_pattern0[] PROGMEM = {0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0x10, 0x20, 0x40, 0x80, 0xff, 
									   	                      0, 0x01, 0x02, 0x04, 0, 0x08, 0x10, 0x20, 0x40, 0x80, 0xff, 0, 0, 0, 0};


// automatically-generated c struct
const uint8_t thanks_fc[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x08, 0x14, 0x22, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00,
  0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00,
  0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x00, 0x22, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x22, 0x00,
  0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22, 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c, 0x00,
  0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x67, 0x41, 0x6f, 0x45, 0x58, 0x46, 0x5e, 0x44, 0x55, 0x43, 0x5c, 0x44, 0x56, 0x43,
  0x4a, 0x44, 0x55, 0x43, 0x5c, 0x4a, 0x44, 0x54, 0x5c, 0x4d, 0x4d, 0x43, 0x4a, 0x41, 0x44, 0x43,
  0x56, 0x44, 0x41, 0x45, 0x50, 0x44, 0x67, 0x47, 0x4c, 0x41, 0x44, 0x47, 0x6f, 0x5c, 0x54, 0x50,
  0x5c, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};


// automatically-generated c struct
const uint8_t bounce_1954[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x08, 0x14, 0x22, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00,
  0x1e, 0x22, 0x22, 0x1e, 0x0a, 0x12, 0x22, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
  0x08, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0c, 0x00,
  0x18, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x1c, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x76, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x48, 0x78,
  0x42, 0x48, 0x60, 0x41, 0x48, 0x44, 0x42, 0x48, 0x50, 0x41, 0x48, 0x64, 0x42, 0x48, 0x70, 0x41,
  0x48, 0x60, 0x49, 0x48, 0x54, 0x42, 0x48, 0x60, 0x41, 0x48, 0x4c, 0x40, 0x4c, 0x40, 0x41, 0x48,
  0x60, 0x54, 0x40, 0x40, 0x51, 0x48, 0x50, 0x4e, 0x40, 0x46, 0x51, 0x48, 0x70, 0x4e, 0x40, 0x46,
  0x41, 0x48, 0x70, 0x43, 0x48, 0x48, 0x42, 0x48, 0x70, 0x41, 0x48, 0x4c, 0x42, 0x48, 0x50, 0x5e,
  0x40, 0x76, 0x41, 0x48, 0x70, 0x59, 0x48, 0x68, 0x42, 0x48, 0x70, 0x43, 0x48, 0x58, 0x49, 0x48,
  0x50, 0x42, 0x48, 0x50, 0x40, 0x4c, 0x40, 0x41, 0x48, 0x60, 0x54, 0x40, 0x40, 0x45, 0x48, 0x60,
  0x59, 0x48, 0x74, 0x4e, 0x40, 0x78, 0x5e, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x40, 0x68,
  0x46, 0x54, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x4b, 0x42, 0x48,
  0x60, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x59, 0x5f, 0x7f, 0x40, 0x42, 0x40,
  0x42, 0x40, 0x40, 0x59, 0x5f, 0x7f, 0x46, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x40, 0x46,
  0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x40, 0x68, 0x40, 0x41, 0x60, 0x00,
  0x00, 0x00, 0x00, 0x5e, 0x40, 0x60, 0x5e, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};



struct index_struct {
  char *pname;
  char *pattern;
  int len;
  int repetitions;
};

struct index_struct index[] = {
  {"test pattern", tape_pattern0, sizeof(tape_pattern0), 4},
  {"Bounce-1954", bounce_1954, sizeof(bounce_1954), 1},
  {"Donor Thanks", thanks_fc, sizeof(thanks_fc), 1},
};
int index_len = sizeof(index) / sizeof(struct index_struct);


#define BUFLEN 256    // buffer for holding input characters prior to punch
static char input_buf[BUFLEN];
static uint16_t input_bytes = 0;   // count of what's in the buffer


void setup() {
  uint8_t blinker;
  uint8_t i;

  pinMode(PUNCH_ADVANCE_PIN, OUTPUT);
  digitalWrite(PUNCH_ADVANCE_PIN, 0);
  for (i = 0; i < 8; i++) {
    pinMode(PUNCH_TRACK_0_PIN + i, OUTPUT);
    digitalWrite(PUNCH_TRACK_0_PIN + i, LOW);
  }

  Serial.begin (9600);
  Serial.print("begin\n");

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(STROBE_LED_PIN, OUTPUT);
  pinMode(SYNC_PULSE_PIN, INPUT);

  pinMode(ONE_HOT_BUTTON_PIN, INPUT_PULLUP);  // pushbutton for starting test sequence
  pinMode(RAND_PUNCH_SW_PIN, INPUT_PULLUP);  

  // blink a little 'hello' signal
  for (blinker = 0; blinker < 5; blinker++) {
    digitalWrite(STROBE_LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    digitalWrite(LED_PIN, HIGH);
    delay(200);
    digitalWrite(STROBE_LED_PIN, LOW);
    digitalWrite(LED_PIN, LOW);
    delay(200);
  }
  fast_srand(0);  // initialize the RNG
  printf("punch ready to go\n");
}


#define MAX_SPIN_WAIT (100000 / 4)    // 100K spin loops, about 4 usec per loop 
#define BIG_INT 0xFFFFFFF;  // 28 bits, i.e., a lot

static uint32_t g_min_wait_time = BIG_INT;  // 28 bits, i.e., a lot

void punch_a_byte(uint8_t punch_code, int wait_cycles) {
  uint8_t sync;
  uint8_t i;
  uint32_t spin_wait;
  int wait_start_time;

  while((digitalRead(SYNC_PULSE_PIN) & 0x1) == 0);  // don't start in the middle of a sync pulse

//  printf("punch_a_byte 0x%x, wait %d\n", punch_code, wait_cycles);
  while (wait_cycles >= 0) {
    wait_start_time = millis();
    spin_wait = 0;
    while((digitalRead(SYNC_PULSE_PIN) & 0x1) == 1) {  // wait for a zero; i.e., spin while one
      spin_wait++;
      if (spin_wait > MAX_SPIN_WAIT) {
        printf("sync timeout punching 0x%02x; %d ms\n", punch_code, (millis() - wait_start_time));  // yeah ok, bogus result when millis() rolls over
        return;
      }
    }
    if (spin_wait < g_min_wait_time) {   // make sure we're not "just barely" making the timing
      g_min_wait_time = spin_wait;
    }
    // at this point, we're just past the cycle start
  
    if (wait_cycles == 0) {  // activate punch magnets
      for (i = 0; i < 8; i++) {
        digitalWrite(PUNCH_TRACK_0_PIN + i, punch_code & 1);
        punch_code >>= 1;
      }
      digitalWrite(PUNCH_ADVANCE_PIN, 1);
    }
    wait_cycles--;  // when this goes negative, we're done waiting, and the magnets are activated

    // delay(MAGNET_ACTIVATE_MS); // wait while the magnets activate.  Note that the driver adds two msec or so of turn-off delay
    // seems that the Advance magnet needs to be disabled early to avoid extra bytes, while the track magnets need to hold a bit
    // longer to avoid dropped bits.
    delayMicroseconds(1000);
    digitalWrite(STROBE_LED_PIN, LOW);
    digitalWrite(PUNCH_ADVANCE_PIN, 0);
    delayMicroseconds(1500);
    for (i = 0; i < 8; i++) {
      digitalWrite(PUNCH_TRACK_0_PIN + i, LOW);
    }
    // delay(1);  make sure it's not possible to get back to the start during the same sync
  }
}



// this routine punches a series of codes 0, 1, 2, 4, 8, 16, 32, 64, 128, i.e., a blank
// followed by one bit per character
void punch_one_hot(uint8_t punch_count, uint8_t cycle_wait_count) {
  static uint8_t cycle_count = 0;
  uint8_t i;
  static uint8_t punch_code = 0;
  
  while (punch_count > 0) {
    punch_code = 1 << (cycle_count & 0x7);
    punch_a_byte(punch_code, cycle_wait_count);

    punch_count-- ;  // only count the holes we punch against the count
  
    cycle_count += 1;
    if (cycle_count <= 32)
      digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    if (cycle_count > 32)
      digitalWrite(LED_PIN, LOW);
    if (cycle_count >= 64)
      cycle_count = 0;
  }
}


#define LEADER 50  // punch this many bytes at the start of a rand sequence.

void punch_rand(uint8_t count, uint8_t insert_wait, uint8_t start_over) {
  int32_t rng;
  uint8_t wait_rng;
  uint8_t punch_rng;
  uint8_t punch_code;
  uint8_t wait_cycles;
  uint8_t leader_bytes;

  if (start_over) {
    if (chatty) printf("punch %d leader bytes\n", LEADER);
    for (leader_bytes = LEADER; leader_bytes > 0; leader_bytes--) {
      if (leader_bytes == 1)
        punch_code = 0xff;
      else
        punch_code = 0;
      punch_a_byte(punch_code, 0);
    }
    fast_srand(0);  // initialize the RNG
  }

  while (count > 0) {
    rng = fast_rand();
    punch_rng = rng & 0xff;
    wait_rng = (rng >> 8) & 0xff;
    wait_cycles = 0;
    if (TRUE) {  //(insert_wait) {
      if ((wait_rng & 0x70) == 0) {
        wait_cycles = wait_rng & 0x7;
        if (chatty) printf("wait: %d\n", wait_cycles);
      }
    }
    if (chatty) printf("punch_rng: %x, wait_rng: %x, wait_cycles %d\n", punch_rng, wait_rng, wait_cycles);
    punch_a_byte(rng, wait_cycles);
    count--;
  }  
}

void punch_from_index(char c) {
  uint8_t offset;
  uint8_t *patternp;
  uint8_t pcode;
  int len, i;
  int reps;
  
  offset = c - '0';
  if (offset >= index_len) {
    printf("pre-programmed punch index %d out of range\n", offset);
    return;
  }
  for (reps = 0; reps < index[offset].repetitions; reps++) {
    printf("pattern %d %s, rep %d\n", offset, index[offset].pname, reps);
    patternp = index[offset].pattern;
    len = index[offset].len;
    for (i = 0; i < len; i++) {
      pcode = pgm_read_byte_near(patternp + i);
      punch_a_byte(pcode, DELAY_CYCLES);    /* insert delay cycles to avoid ripping fragile tape */
    }
  }
  printf("\n");
}


/* **********************************************
 *  Main Loop
 *********************************************** */
void loop() {
  static uint8_t start_rand = TRUE;
  static uint8_t was_rand = FALSE;
  static uint16_t loop_count = 0;
  int c, count, i;
  uint8_t punch_code;

  if ((digitalRead(ONE_HOT_BUTTON_PIN) & 0x1) == 0) {
    printf("punch one-hot pattern\n");
    punch_one_hot(33, 15);
  } else if ((digitalRead(RAND_PUNCH_SW_PIN) & 0x1) == 0) {
    printf("punch random pattern\n");
    punch_rand(33, TRUE, start_rand);
    start_rand = FALSE;
    was_rand = TRUE;
    printf("min spin cycles = %ld cycles, ~ %ld usec\n", g_min_wait_time, g_min_wait_time * 4);
    g_min_wait_time = BIG_INT;  // reset the statistic collector
    delay(300);
  } else if (((digitalRead(RAND_PUNCH_SW_PIN) & 0x1) == 1) && was_rand) {  // catch the first cycle after the rand switch was on
    was_rand = FALSE;
    start_rand = TRUE;
    punch_rand(0, TRUE, start_rand);
  } else if (Serial.available() > 0) {
    c = Serial.read();
    if (c == 'Z') {
      count = collect_serial_buf(&input_buf[0], BUFLEN);
      if (count < 0) {
        printf("collect_serial_buf failed ret=%d\n", count);
      } else {
        /* do this output loop twice to avoid intermixing the error messages if the
         *  punch motor isn't turning
         */
        printf("punch %d bytes: ", count);
        for (i = 0; i < count; i++) {
          punch_code = input_buf[i];
          printf("0x%02x ", punch_code);
        }
        printf("\n");
        for (i = 0; i < count; i++) {
          punch_code = input_buf[i];
          punch_a_byte(punch_code, 0);
        }
      }
    } else if (c >= '0' && c <= '9') {
      punch_from_index(c);
    } else if (c == 'l') {  /* "list" the pre-build tapes to punch */
      for (i = 0; i < index_len; i++) {
        printf("  Index %d: %s\n", i, index[i].pname);
      }
    } else if (c != '\n' && c != '\r') {
      printf("unexpected command 0x%x '%c'\n", c, c);
    }
  }
  
  else {
    digitalWrite(LED_PIN, (loop_count & 0x1)); // blink
    if ((loop_count % 10) == 0)
      printf("no-op %06d\n", loop_count);
    loop_count++;
    delay(500);
  }
}






/* ************************************************************ */
/* **  Serial Input  */


/* return the number of bytes collected in ASCII-Hex format, from the 
 *  serial port, up to a CR or LF,
 * or a negative return code
  */
int collect_serial_buf(char *bufp, int buf_size) {
  int c;
  int offset;

  offset = 0;

  if ((c = get_serial_char()) < 0) 
    return(-1);
  if (c != 'Z') {
    printf("expected a second 'Z' to start load; got %c\n", c);
    return(-2);
  }

  while((c = get_serial_hex_byte()) >= 0) {
    if (c < 0) 
      break;
    bufp[offset++] = c;
    if (offset >= buf_size) {
      printf("buffer size overrun at %d bytes\n", buf_size);
      break;
    }
  }
  return(offset);
}


/* read two hex characters from the serial port and assemble them into a byte
 */
int get_serial_hex_byte() {
  int s[2];
  int c;
  int8_t i;
  int ret;
  uint8_t eol;

  eol = FALSE;
  for (i = 0; i < 2; i++) {
    c = get_serial_char();
    if (c < 0)
      return(-1);
    if (c >= '0' && c <= '9') {
      s[i] = c - '0';
    }
    else if (c >= 'A' && c <= 'F') {
      s[i] = c - 'A' + 10;
    }
    else if (c >= 'a' && c <= 'f') {
      s[i] = c - 'a' + 10;
    }
    else if (c == '\n' || c == '\r') {  /* newline or CR is treated as End of Message */
      eol = TRUE;
      break;
    }
    else {
      printf("non-hex character '%c'\n", c);
      return(-2);
    }
  }
  if (i == 1 && eol == TRUE) {
    printf("odd number of characters in last byte\n");
    return(-2);
  }
  if (eol)
    return(-1);
  ret = (s[0] << 4 | s[1]) & 0xFF;
  return(ret);
}


#define SERIAL_WAIT_TIMEOUT 5000
int get_serial_char() {
  long wait_start_time, now;

  wait_start_time = millis();
  while(1) {
    if (Serial.available() > 0) {
      return(Serial.read());
    }
    now = millis();
    if ((now - wait_start_time) > SERIAL_WAIT_TIMEOUT) {
      printf("serial character wait timeout\n");
      break;
    }
  }
  return(-1);
}




/* Random number generator */
// https://stackoverflow.com/questions/52514296/simplest-random-number-generator-without-c-library
static uint32_t g_seed = 0;

// Used to seed the generator.           
inline void fast_srand(uint32_t seed) {
    g_seed = seed;
}

// Compute a pseudorandom integer.
// Output value in range [0, 32767]
inline int fast_rand(void) {
    g_seed = (214013*g_seed+2531011);
    return (g_seed>>16)&0x7FFF;
}
 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: hOGrqaHZX0zrKxoY (1).png
Type: image/png
Size: 858605 bytes
Desc: not available
URL: <http://mailman.qth.net/pipermail/greenkeys/attachments/20231231/fd0225ff/attachment-0001.png>


More information about the GreenKeys mailing list