Jump to content
IGNORED

Remote control of TI via keyboard


speccery

Recommended Posts

I have had for a while the desire to use an external keyboard with the TI, mainly to have more flexibility on my desk where to place the console. For the display I'm already using F18A board connected to VGA-to-HDMI converter connected to a USB3 HDMI grabber, so the TI's screen is visible as a Window on my Mac's desktop. If I had something similar for the keyboard, I could move the TI to the side, and just control it from my Mac's keyboard.

 

There are existing great projects by @jedimatt42 and @Tursi to use USB or PS/2 keyboards with the TI. The TI99USBKeys USB host project is very desirable, but don't have a Teensy 3.1 or 3.2 board, and when searching for those I didn't find the boards anywhere available for purchase. I started thinking that even if I had that, I would end up having another USB keyboard on my desk to control the TI. So I thought I actually want something simpler, just a way to transfer keyboard information from my Mac to the TI. I also thought that since I already have many (and I mean many, I have lost count ages ago) development boards already, why not use something that I already have.

 

I build a contraption which consists of a Nucleo-F767 board inside the TI, connected via USB to my Mac. This board connects to the keyboard connector of the TI, and basically just receives key codes from the Mac and presents them to the TI the way it understands them. Unlike many of my other projects, this one is simple, straightforward and started to work quickly. 

ti-kbd-device.thumb.jpg.0a2eb239190feb5c144ead34d63aea13.jpg

 

I initially controlled the keyboard with a simple terminal emulator program running on the Mac, but I quickly ran into problems. It was difficult to support shift keys and the like using a terminal program. Also key-up events don't exist with a normal terminal program... To remedy this I ditched the terminal program and wrote quickly a small app in C++ using the SDL2 framework for the Mac. This app just listens to keyboard presses and sends key up and key down events to the Nucleo board. Of course this meant developing yet another simple communication protocol... SDL2 is cross platform, so it would be easy to port the program to other operating systems.

 

The key transfer protocol is very simple, and consists of two byte messages sent over USB serial port at 115200 bps:

// My command codes. Transfer format contains two bytes:
// one of the below, followed by the keycode.
#define SERIAL_KEYDOWN  '1'
#define SERIAL_KEYUP    '0'
#define SERIAL_ALLUP    '2'

// The keycodes following the command code above:
// These are mostly just ASCII codes, except for certain keys listed below.
// TI specific keycodes for a few keys, selected not to collide with SDL key codes
// used here.
#define TI_SHIFT        SDL_SCANCODE_LSHIFT   // 225
#define TI_ALPHA_LOCK   SDL_SCANCODE_CAPSLOCK // 57
#define TI_CTRL         SDL_SCANCODE_LCTRL    // 224
#define TI_FCTN         SDL_SCANCODE_LALT     // 226
#define TI_LEFT         128
#define TI_RIGHT        129
#define TI_UP           130
#define TI_DOWN         131

I haven't had yet much time to use setup, but it seems to work and is very convenient to use just one (did I say modern) keyboard to control both the Mac and the TI. I mapped the ESCape key to signal that all keys are forced up, to overcome issues where there might be a stuck key. It can happen when switching apps on the Mac with option-TAB.

 

The Nucleo-F767 board is very powerful and huge overkill for this task. I chose it because among the development boards I have, it has very many 5V tolerant pins and it has an USB-to-go port. I am not using that port yet, but using it the board can support directly connected USB keyboards too, and STmicro has an example program which has the HID driver to be an USB host. I did try out that example, but I haven't integrated it and I am not sure if I will do that since for my use case this setup already works.

  • Like 14
Link to comment
Share on other sites

That's pretty cool. With my teensy 3.2 design, the USB host keyboard code eats up most of the cycles of the ARM processor. Consequently I can still miss the TI scanning the keyboard such that I have the columns triggering an interrupt, but then the interrupt handler has to read the current column as it can be different already... 

 

One feature you probably had with the previous terminal interface was paste... I'm curious if you think about adding a paste mode with your SDL app? 

 

 

---

 

You need a cover like the old Rave99 keyboard interface and you'll be also set... 

 

http://mainbyte.com/ti99/hardware/rave/both.jpg

 

I have no idea what those holes were for... 

  • Like 2
Link to comment
Share on other sites

Now this is a good idea ... I also get confused with more then one keyboard in front of me. I have created a (wireless) USB keyboard interface for my TI99/4A and Geneve arround a Teensy 3.2 and I'm also going to do this and try it first with an Arduino Uno. If that is not fast enough I go back to the Teensy 3.2 (or something else that is small) :-)

 

 

  • Like 4
Link to comment
Share on other sites

On 7/23/2022 at 9:05 PM, InfiniteTape said:

With this setup, you could probably also tie to the FinalGROM's reset buttons, then make yourself a key combo on the remote side to trigger them.

That’s an excellent idea! Might do the same for StrangeCart, which is what I use.

  • Like 2
Link to comment
Share on other sites

On 7/20/2022 at 8:00 AM, jedimatt42 said:

That's pretty cool. With my teensy 3.2 design, the USB host keyboard code eats up most of the cycles of the ARM processor. Consequently I can still miss the TI scanning the keyboard such that I have the columns triggering an interrupt, but then the interrupt handler has to read the current column as it can be different already... 

 

One feature you probably had with the previous terminal interface was paste... I'm curious if you think about adding a paste mode with your SDL app? 

 

 

---

 

You need a cover like the old Rave99 keyboard interface and you'll be also set... 

 

http://mainbyte.com/ti99/hardware/rave/both.jpg

 

I have no idea what those holes were for... 

Thanks @jedimatt42 for the comments and sorry for the slow reply! My TI-99/4A is in a space which doubles as our guest room, and due to that I haven't had much access in the past several weeks to the TI. Nice to have kids over :) 

 

Yes, I am planning to add the paste mode indeed. I've already missed that feature. Also thing about generic keystroke recording feature: push some key combo, and then the following keypresses would be recorded, until another key combo finishes that. After that you could replay the sequence with another key combo.

 

With regards to scanning the keyboard row/column selects, my program is very simple: it just sits in a tight loop polling the selects. Key receive from the SDL app is done in an interrupt service routine. I do want to change the select scanning to be interrupt driven as well, good point to check in the ISR again the status.

  • Like 1
Link to comment
Share on other sites

On 7/23/2022 at 8:18 PM, F.G. Kaal said:

Now this is a good idea ... I also get confused with more then one keyboard in front of me. I have created a (wireless) USB keyboard interface for my TI99/4A and Geneve arround a Teensy 3.2 and I'm also going to do this and try it first with an Arduino Uno. If that is not fast enough I go back to the Teensy 3.2 (or something else that is small) :-)

I'm planning to port my code over to some smaller Nucleo boards, I have several Nucleo-G431KB boards which are of the same size as Arduino nano (if that's what the Arduino is called). I didn't originally go with that board since it doesn't have an USB OTG port, but in my use case it's not really necessary. Also, I'm not sure if it has enough 5V tolerant pins, may need to add a buffer chip as well. Even with that it would be very small.

  • Like 1
Link to comment
Share on other sites

22 hours ago, speccery said:

I'm planning to port my code over to some smaller Nucleo boards, I have several Nucleo-G431KB boards which are of the same size as Arduino nano (if that's what the Arduino is called). I didn't originally go with that board since it doesn't have an USB OTG port, but in my use case it's not really necessary. Also, I'm not sure if it has enough 5V tolerant pins, may need to add a buffer chip as well. Even with that it would be very small.

Created a little terminal program this week (stripped down an existing terminal I wrote many many years ago)

afbeelding.thumb.png.f8f6623858af0b11303c6a8fa02b5564.png

(yellow is what type, light blue is what the Teensy made of it, SCA (?) is Key up/down numl, capsl, ctrl, alt, shift).

 

Stripped down the Teensy 3.2 version of my Geneve USB keyboard converter (from 35Kb down to 9Kb, just removing all the SPI and USB host libraries), but to my surprise the amount of RAM needed is arround 5Kb and that is to much for an Arduino Uno or Nano. There are still many things I can clean up in the code ... all debug messages and conversion tables are in RAM while these are supposed to be const and can be moved to PROGMEM. Then I hope that all those fancy Mdos and Basic keyboard modes still fit in the flash ROM (32Kb) en RAM (2.4Kb) of an Arduino otherwise I stick to a Teensy kind of board (all IO 5v compliant).

 

Started with the Geneve version because this is the most simple one. For the TI99 version the base is allmost the same but the TI-side is a little bit trickier with the interrupt driven keyboard scan on the Teensy (or Arduino side).

 

  • Like 4
Link to comment
Share on other sites

I added copy-paste (meaning the paste part) to my SDL front end app. Seems to work. Haven't done anything for real with it, but quite cool to be able to just quickly paste stuff from the Mac to the TI. I wrote some code to convert ASCII to TI keycodes to make this work.

 

Below is the gist of the C++ code converting from ASCII to key codes.

Spoiler

std::list<uint8_t> paste_keys;  // Here are the keypresses to be pasted.


void insert_key_up_down(char c) {
  paste_keys.push_back(SERIAL_KEYDOWN);
  paste_keys.push_back(c);
  paste_keys.push_back(SERIAL_KEYUP);
  paste_keys.push_back(c);
}

void insert_shifted_key_up_down(char c) {
  paste_keys.push_back(SERIAL_KEYDOWN);
  paste_keys.push_back(TI_SHIFT);
  
  paste_keys.push_back(SERIAL_KEYDOWN);
  paste_keys.push_back(c);
  paste_keys.push_back(SERIAL_KEYUP);
  paste_keys.push_back(c);
  
  paste_keys.push_back(SERIAL_KEYUP);
  paste_keys.push_back(TI_SHIFT);
}

void insert_fctn_key_up_down(char c) {
  paste_keys.push_back(SERIAL_KEYDOWN);
  paste_keys.push_back(TI_FCTN);
  
  paste_keys.push_back(SERIAL_KEYDOWN);
  paste_keys.push_back(c);
  paste_keys.push_back(SERIAL_KEYUP);
  paste_keys.push_back(c);
  
  paste_keys.push_back(SERIAL_KEYUP);
  paste_keys.push_back(TI_FCTN);
}


bool queue_ascii_paste(char c) {
  if(c >= '0' && c <= '9') {
    insert_key_up_down(c);
    return true;
  }
  if(c >= 'a' && c <= 'z') {
    insert_key_up_down(c - ('a' - 'A'));
    return true;
  }
  if(c >= 'A' && c <= 'Z') {
    insert_shifted_key_up_down(c);
    return true;
  }
  switch(c) {
    case '.':
    case ',':
    case ';':
    case ' ':
    case '/':
    case '=':
      insert_key_up_down(c);
      return true;
    case '<': insert_shifted_key_up_down(','); return true;
    case '>': insert_shifted_key_up_down('.'); return true;
    case ':': insert_shifted_key_up_down(';'); return true;
    case '-': insert_shifted_key_up_down('/'); return true;
    case '+': insert_shifted_key_up_down('='); return true;
    case '!': insert_shifted_key_up_down('1'); return true;
    case '@': insert_shifted_key_up_down('2'); return true;
    case '#': insert_shifted_key_up_down('3'); return true;
    case '$': insert_shifted_key_up_down('4'); return true;
    case '%': insert_shifted_key_up_down('5'); return true;
    case '^': insert_shifted_key_up_down('6'); return true;
    case '&': insert_shifted_key_up_down('7'); return true;
    case '*': insert_shifted_key_up_down('8'); return true;
    case '(': insert_shifted_key_up_down('9'); return true;
    case ')': insert_shifted_key_up_down('0'); return true;
    case '\n': insert_key_up_down('\r'); return true;
    case '\r': return true;
    case '_': insert_fctn_key_up_down('U'); return true;
    case '"': insert_fctn_key_up_down('P'); return true;
  }
  return false; // Don't know what to do here.
}


// excerpt - code to convert ASCII to the paste queue
            int r = get_paste_data(paste_buf, sizeof(paste_buf));
            if(r > 0) {
              std::cout << "PASTE(" << r << "): " << paste_buf << std::endl;
              for(char *p = paste_buf; *p && p-paste_buf < sizeof(paste_buf); p++)
                queue_ascii_paste(*p);
              original_queue_len = paste_keys.size();
              std::cout << "Original len: " << original_queue_len << std::endl;
            } else
              std::cout << "PASTE returned " << r << std::endl;
          }

 

 

  • Thanks 1
Link to comment
Share on other sites

In my situation, we had storms for 2 days, power was dropping and a quick power off for my raspberry on TIPI is to call tipi and shutdown.

But I didn't know if my pi had already been screwed up or not, and my PEB is on top of a shoulder hi bench that has a printer sitting on top of it and it's not very easy to get access to the little 5 volts plug. I've yet to add something to remedy that, all is powered by a quick switch on a power outlet on the floor - in case I've already shut down the pi and can power it back up. Usually I do this through the call tipi menu of shutdown and later Power up with the switch.

Anyway, none of that is tipi issue, but calling tipi and getting the screen says TIPI is up and I can proceed, but waiting for ip takes time.

 

My original statement about wifi out of range isn't all that true, as it's in range but not when we have storms, usually my local phone company has already dropped router connection because they suck during a rain storm. Just the other day Lee and I were on zoom and I was kicked off the router because that's life during these threats of storms here.

Power stayed up, but never the router signals. This is normal when storms hit here. I payed extra for that convenience..I'm being a SA now...

Using a phone as a hotspot..not when I'm paying locally for WiFi and paying another bill for phone. I don't always have cell service as it depends if I can afford it for that month. WHY? Because I'm only out of the house mmm..like 2 days a week and if we need stuff, I just send the kids out..so I don't always have service on a cell. My landline is hot whenever my router is up.

I also have a rasberry pi running Debian and it also waits for ip before booting, but does timeout and it's probably a minute or so..I think theres actually a setting for that too, as it might be able to be ignored.

Edited by GDMike
Link to comment
Share on other sites

  • 6 months later...

While recovering from the flu, I realised that a board I've been working on happens to have 15+ I/O pins on one edge. I've been using the keyboard connection solution depicted in the first message of this thread since July and it's been working great, but it's clumsy and untidy. So I ported the code to this other board I created and got it working today. Much tidier, when I have more energy I will try to see if I can fit this and the keyboard at the same time. The white extra wire is the ground wire, the TI keyboard connector doesn't have a ground connection. I'm hoping the TI keyboard cable will get a good connection on top of this extra board. Even if it doesn't, this is still so much more convenient.

image.thumb.jpeg.eb89bf6512c62aaead219cb41dfdf766.jpeg

  • Like 6
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...