PICO VBAND

Ref : https://github.com/f4goh/pico_vband

oplus_32

schema

Code

// SPDX-License-Identifier: MIT
//
// VBAND CW USB dongle based on a Pi Pico.
// Also supports other key outputs for Vail and morsecode.me etc.
//
// Inspired from similar projects at:
// https://github.com/sipsmi/vband_dongle/tree/main
// https://github.com/mgiugliano/MorsePaddle2USB
// https://github.com/nealey/vail-adapter
//
// There is also a PiPico circuit python implementation shown on 
// https://www.qrz.com/db/KG5QNO


#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <ClickButton.h>	// https://github.com/marcobrianza/ClickButton
#include <singleLEDLibrary.h>	// https://github.com/SethSenpai/singleLEDLibrary
#include <arduino-timer.h>	// https://github.com/contrem/arduino-timer

#define VERSION 1.1

#define DEBUG 0

// Pick your dit and dah pins - we go with the normal 'Dah on the right' convention
#define LEFT_PIN 3  //Dit
#define RIGHT_PIN 2 //Dah

// Mode/multi-function button pin
#define BUTTON_PIN 4

//#define LED_PIN LED_BUILTIN
// If you want a convinient external LED connection then I used GPIO9/pin12 along with the convinient
// GND on pin13. Enabling both LEDs at once is not completely trivial with the singleLEDLibrary, so
// right now you can have one or the other (onboard or outboard).
#define LED_PIN 8   // 9 physically convenient next to a gnd

// We are going to try and support:
// vband - https://hamradio.solutions/vband/
// vail - https://vail.woozle.org/#
// morsecode.me - https://morsecode.me/#/help/practice

// VBAND can use a number of different key combinations in the web browswer:
// - [ ]
// - CTRL-L CTRL-R

#define VBAND_LEFT '['
#define VBAND_RIGHT ']'

// Vail can use a number of different key combinations in the web browswer:
// - [ ]
// - CTRL-L CTRL-R
// - . /
// - x z
// We are deliberate choosing some different to VBAND
#define VAIL_LEFT 'x'
#define VAIL_RIGHT 'z'

// morsecode.me feels like it only supports a straight key, and has a number of keyboard
// chars it supports
// - .
// - i
// - e
// - [space]
// We are deliberate choosing something different to the others
#define MORSECODE_LEFT 'e'
#define MORSECODE_RIGHT 'i'

// And finally, support the CTRL key mode, as that can in some cases
// work I think when the target window does not even have focus, which
// might be useful
#define CTRL_LEFT KEY_LEFT_CTRL
#define CTRL_RIGHT KEY_RIGHT_CTRL

#define LEFT_INDEX 0  //Dit
#define RIGHT_INDEX 1 //Dah

char mode_array[][2] = {
  {VBAND_LEFT, VBAND_RIGHT},
  {VAIL_LEFT, VAIL_RIGHT},
  {MORSECODE_LEFT, MORSECODE_RIGHT},
  {CTRL_LEFT, CTRL_RIGHT}
};

int current_mode = 0; //Default to VBAND
const int max_mode = sizeof(mode_array)/sizeof(mode_array[1]);

#define MODE_BLINK_ON_TIME 100
#define MODE_BLINK_OFF_TIME 50
// Make sure this array is at least twice as long as the number of modes we have,
// as it encompasses both on/off times
int ledflash[] = {
  MODE_BLINK_ON_TIME, MODE_BLINK_OFF_TIME,
  MODE_BLINK_ON_TIME, MODE_BLINK_OFF_TIME,
  MODE_BLINK_ON_TIME, MODE_BLINK_OFF_TIME,
  MODE_BLINK_ON_TIME, MODE_BLINK_OFF_TIME,
  MODE_BLINK_ON_TIME, MODE_BLINK_OFF_TIME
};

//Our mode change button
ClickButton mode_button(BUTTON_PIN, LOW, CLICKBTN_PULLUP); //Active low button

//Our activity indicator led
sllib led(LED_PIN);

auto timer = timer_create_default();

//Track key states so we can flash LED in time.
bool left_down = false;
bool right_down = false;

//Try to debounce the keys a bit. Let's see if we only need to debounce from the down time - that is,
// ignore any 'ups' for this amount of time after the first down.
unsigned long key_debounce_ms = 10;
unsigned long left_down_ms = 0;
unsigned long right_down_ms = 0;

//Used with the timer blob to turn off some LED activity after a specified
//period - mainly because the LED library does not have oneshot calls sadly
bool cancel_leds(void *arg) {
  led.setOffSingle();
  return true;
}

// setup the pins and HID (device)
void setup() {
  if (DEBUG) { Serial.begin(115200); delay(3000); }
  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
  Keyboard.begin(); // initialise HID library

  mode_button.longClickTime = 350;  //Shorter time for a long click, not 1s default

  //We presume we start with the keys not down!, so don't bother reading them here.
  //left_down = digitalRead(LEFT_PIN);
  //right_down = digitalRead(RIGHT_PIN);

  //We start with a 'heartbeat' to show we are alive. Once a key is pressed this will stop and we move into
  //flashing the LED for each further activity.
  led.setBreathSingle(1000);  //We start with the breathing LED.
}

void loop(){
  int clicks;
  bool left_current = false;
  bool right_current = false;

  timer.tick();
  led.update();
  mode_button.Update();

  if (mode_button.changed ) {
    clicks = mode_button.clicks;
    
    if (DEBUG) Serial.println(" changed " + String(mode_button.changed) + ":" + String(mode_button.clicks));    
  
    if (clicks != 0 ) {
      if (DEBUG) Serial.println("click " + String(clicks));
      if (clicks > 0 ) {
        //short click(s) - cycle through the modes
        current_mode = (current_mode + clicks) % max_mode;
        
        if (DEBUG) Serial.println(" mode " + String(current_mode));
        led.setPatternSingle(ledflash, (current_mode+1)*2);  //Flash LED with mode no.
        int flash_length = ((current_mode+1)* MODE_BLINK_ON_TIME) +
          ((current_mode+1)* MODE_BLINK_OFF_TIME);
        timer.in(flash_length, cancel_leds);
      } else {
        //Long press - reset to mode0
        current_mode = 0;
        
        if (DEBUG) Serial.println(" reset mode " + String(current_mode));
        led.setPatternSingle(ledflash, (current_mode+1)*2);  //Flash LED with mode no.
        int flash_length = ((current_mode+1)* MODE_BLINK_ON_TIME) +
          ((current_mode+1)* MODE_BLINK_OFF_TIME);
        timer.in(flash_length, cancel_leds);
      }
    }
  }

  //We should explain our debounce philosophy here a little.
  //Normally debounce routines wait for a set period of time (say, 10ms)
  //and look for 'stability' of the button value before registering the
  //button (key) has been pressed. What I want is to know as soon as the
  //button is pressed (or released), so acknowledge the press/release as
  //soon as we detect a change in state - but then to avoid the following
  //potential bouneces, we ignore all other changes for a set time (say, 10ms).
  //
  //One potential downside of this method is that it is likely not noise immune.
  //That is, if some noise turns up and briefly twiddles the pin, we will take that
  //as a press or release. But, that's the price we pay.
  left_current = digitalRead(LEFT_PIN) == LOW;  //active low!
  right_current = digitalRead(RIGHT_PIN) == LOW;  //active low!

  if (left_current != left_down ) { //did the key change state
    unsigned long ms = millis();
    
    if (DEBUG) Serial.println("LEFT change: " + String(left_current) + " to " + String(left_down));

    //Are we still in a debounce time slot...
    if (left_down_ms + key_debounce_ms > ms ) {
      //Still debouncing
      if (DEBUG) Serial.println(" deb " + String(ms - left_down_ms));
    } else {    
      //Not debouncing, so lets process it
      if (DEBUG) Serial.println(" process " + String(ms - left_down_ms));
      if (left_current) { //button down
        left_down_ms = ms;
        //led.setBlinkSingle(5000); //A hack - as the old version of the library is missing 'On'
        digitalWrite(LED_PIN, HIGH);
        if (DEBUG) Serial.println("LEFT down");
        Keyboard.press(mode_array[current_mode][LEFT_INDEX]);        
      } else {
        //Button up
        left_down_ms = ms;
        led.setOffSingle();
        if (DEBUG) Serial.println("LEFT up");
        Keyboard.release(mode_array[current_mode][LEFT_INDEX]);      
      }
      //And remember which state we are in...
      left_down = left_current;
    }
  }

  if (right_current != right_down ) { //did the key change state
    unsigned long ms = millis();
    
    if (DEBUG) Serial.println("RIGHT change: " + String(right_current) + " to " + String(right_down));

    //Are we still in a debounce time slot...
    if (right_down_ms + key_debounce_ms > ms ) {
      //Still debouncing
      if (DEBUG) Serial.println(" deb " + String(ms - right_down_ms));
    } else {    
      //Not debouncing, so lets process it
      if (DEBUG) Serial.println(" process " + String(ms - right_down_ms));
      if (right_current) { //button down
        right_down_ms = ms;
        //led.setBlinkSingle(5000); //A hack - as the old version of the library is missing 'On'
        digitalWrite(LED_PIN, HIGH);
        if (DEBUG) Serial.println("RIGHT down");
        Keyboard.press(mode_array[current_mode][RIGHT_INDEX]);        
      } else {
        //Button up
        right_down_ms = ms;
        led.setOffSingle();
        if (DEBUG) Serial.println("RIGHT up");
        Keyboard.release(mode_array[current_mode][RIGHT_INDEX]);      
      }
      //And remember which state we are in...
      right_down = right_current;
    }
  }
}

Search

Popular Posts

  • KENWOOD TS850S

    https://www.rigpix.com/kenwood/ts850s.htm Le Kenwood TS-850SAT est un émetteur-récepteur HF haut de gamme destiné aux radioamateurs. Lancé au début des années 1990, il est encore aujourd’hui très apprécié pour la qualité de son récepteur, son ergonomie et sa robustesse. Caractéristiques principalesCouverture des bandes amateur HF de 160 à 10 mètres, y compris les bandes WARC.Puissance d’émission de…

  • QRZ.COM Page BIO

    Visu PDF Pages CSS .container{max-width:900px;margin:auto;font-family:Arial, Helvetica, sans-serif;} .card{background:#1e293b;color:#e5e7eb;padding:20px;border-radius:12px;margin-bottom:20px;box-shadow:0 5px 15px rgba(0,0,0,0.3);} .card h1{color:#38bdf8;margin-bottom:10px;} .card h2{color:#7dd3fc;border-bottom:1px solid #334155;padding-bottom:5px;} .badge{display:inline-block;background:#0284c7;color:white;padding:5px 10px;border-radius:8px;margin:4px 2px;font-size:14px;} .gallery {display: grid;grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));gap: 12px;margin-top: 10px;} .gallery img {width: 100%;height: auto;border-radius: 10px;box-shadow: 0 4px 10px rgba(0,0,0,0.4);}a{color:#38bdf8;} a:hover{text-decoration:underline;}#t_bio.biodiv {background-color:#FFFFFF}

Categories

Tags