Радиолюбительский Маяк на arduino nano

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

В разработке конструкция простого радиолюбительского маяка на три диапазона, планируется 144/430/1200Mhz/
За основу скетча планировался проект выполненный радиолюбителем из Италии Nicola Salsotto IN3GJH
Так как скетч был сильно раскритикован здешним сообществом с их помощью и попытаюсь реализовать.

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Начиналось с этого скетча:
 

#define SPEED (20)  //скорость в WPM
#define DOTLEN (1200/SPEED)
#define DASHLEN (4*(1200/SPEED))
//#define PAUSE (180000)  //пауза между передачами маяка в милисекундах
#define PAUSE (10000)  //пауза между передачами маяка в милисекундах

int txPin=10;       //управление PTT
int ledPin=13;      //мигать встроенным светодиодом или подключить внешний на пин 13
int tonePin=5;      //выход звука
int toneFreq=800;   //Частота звука (выбирайте между 800 - 1500Гц)
unsigned long old_millis;

char thePhrase[] = "UA6EM UA6EM BEACON QRA IS LN14AG LN14AG";

typedef struct {
  char letter;
  uint8_t sequence;
} morseCode_t;

//  B11101000 
//   ...-S   <- S - start bit
morseCode_t morseCode[]= {
  {'A', B01100000},  {'B', B11101000},  {'C', B10101000},
  {'D', B11010000},  {'E', B11000000},  {'F', B10111000},
  {'G', B10010000},  {'H', B11111000},  {'I', B11100000},
  {'J', B00011000},  {'K', B01010000},  {'L', B11011000},
  {'M', B00100000},  {'N', B10100000},  {'O', B00010000},
  {'P', B10011000},  {'Q', B01001000},  {'R', B10110000},
  {'S', B11110000},  {'T', B01000000},  {'U', B01110000},
  {'V', B01111000},  {'W', B00110000},  {'X', B01101000},
  {'Y', B00101000},  {'Z', B11001000},  {'0', B00000100},
  {'1', B00001100},  {'2', B00011100},  {'3', B00111100},
  {'4', B01111100},  {'5', B11111100},  {'6', B11110100},
  {'7', B11100100},  {'8', B11000100},  {'9', B10000100},
  {' ', B10000000},
};

void setup() {
    pinMode(ledPin, OUTPUT);
    pinMode(txPin, OUTPUT);
    Serial.begin(115200);
    old_millis = millis();
}

//*** передаём тире ***
void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }

//*** передаём точку ***
void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }

//(c) sadman41 www.arduino.ru 
void sendBeacon(char* _thePhrase) {
  while (*_thePhrase) {
        for(int8_t i=0; i<sizeof(thePhrase); i++)
       {//Serial.println(sizeof(thePhrase));
        // длина определилась верно, проверить при изменении данных
        if (toupper(*_thePhrase) == morseCode[i].letter) {
        uint8_t sequenceStarted = false;
         for (uint8_t l = 0; l < 8; l++) {
         uint8_t currentBit = bitRead(morseCode[i].sequence, l);
        if (!sequenceStarted) {
        sequenceStarted = currentBit; 
        continue; 
              }
     currentBit ? dot() : dash() ;            
             }
           }
          }
          if(toupper(*_thePhrase) == ' ') {Serial.println("Пробел"); delay(3*DOTLEN);}
     _thePhrase++;
     delay(2*DOTLEN);
 }
}

void loop() {
 if(millis() - old_millis >= PAUSE){
     sendBeacon(thePhrase);
     old_millis = millis();
 }
}

Далее:
Вариант 1. (с изменениями от 20.04.2019 - введен режим программирования с консольного порта, вход в режим программирования отправка с консоли "P", далее следуем инструкциям выводимым в консоль, сначала позывной, далее локатор, изменения вступают в силу сразу же, так как функция передачи маяка блокирующая, вход в режим после окончания текущего сеанса
 

#include <EEPROM.h>
int eeAddress = 0;    //EEPROM address to start reading from
int eeFlag = 73;      // в ячейке 73 сохраняем флаг ==73, что структура сохранялась
int eeData;           // сюда читаем байт из ячейки флага
#define DataFlag 73
 
struct MyBeacon {
char call_sign[20];
char loc[7];
};
MyBeacon mb = {"UA6EM", "LN14AG"}; 

String my_call;
String my_loc;
String my_null;
 
#define SPEED (20)  //скорость в WPM
#define DOTLEN (1200/SPEED)
#define DASHLEN (4*(1200/SPEED))
//#define PAUSE (180000)  //пауза между передачами маяка в милисекундах
#define PAUSE (10000)  //пауза между передачами маяка в милисекундах
 
unsigned long old_millis;
unsigned long modeTime;
 
int txPin=10;       //управление PTT
int ledPin=13;      //мигать встроенным светодиодом или подключить внешний на пин 13
int tonePin=5;      //выход звука
int toneFreq=800;   //Частота звука (выбирайте между 800 - 1500Гц)
 
//char thePhrase[] = "ABCDEF GHIJK LMNOP QRSTU VWXYZ 01234 56789 .,:?\/ -()@=";
const int8_t strSize;
volatile char str[50];
//char thePhrase[strSize];
 
//  B11101000
//   ...-S   <- S - start bit
// (c) sadman41 дописал UA6EM www.arduino.ru
typedef struct {
  char letter;
  uint8_t sequence;
} morseCode_t;
// И само наполнение структуры
 morseCode_t morseCode[]= {
  {'A', B01100000},  {'B', B11101000},  {'C', B10101000},
  {'D', B11010000},  {'E', B11000000},  {'F', B10111000},
  {'G', B10010000},  {'H', B11111000},  {'I', B11100000},
  {'J', B00011000},  {'K', B01010000},  {'L', B11011000},
  {'M', B00100000},  {'N', B10100000},  {'O', B00010000},
  {'P', B10011000},  {'Q', B01001000},  {'R', B10110000},
  {'S', B11110000},  {'T', B01000000},  {'U', B01110000},
  {'V', B01111000},  {'W', B00110000},  {'X', B01101000},
  {'Y', B00101000},  {'Z', B11001000},  {'0', B00000100},
  {'1', B00001100},  {'2', B00011100},  {'3', B00111100},
  {'4', B01111100},  {'5', B11111100},  {'6', B11110100},
  {'7', B11100100},  {'8', B11000100},  {'9', B10000100},
  {'.', B01010110},  {',', B00110010},  {':', B11000100},
  {'?', B11001110},  {'-', B01111010},  {'\\', B10000110},
  {'/', B10110100},  {'(', B01001010},  {')', B01001010},
  {'@', B10100110},  {'=', B01110100},  {'"', B10110110},
};
 

//*** передаём тире ***
// (c) Nicola Salsotto IN3GJH
void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }
 
//*** передаём точку ***
// (c) Nicola Salsotto IN3GJH
void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }
 

// *** функция разбора и передачи строки ***
// (c) ЕвгенийП www.arduino.ru
// void sendMsg(char *str){
//  Строка для отправки сформируется внутри,
//  а при выходе будет освобождена, (если не объявить глобально)
//
  void sendMsg(MyBeacon & mbStr) {
  static const char middle[] = "BEACON QRA is"; // серёдка сообщения
  static const int8_t sizeMiddle = strlen(middle);  // длина серёдки сообщения
  static const int8_t repeatCounter = 2;  // сколько раз повторять позывной и ... тот хвост
  // 
  // strSize - длина строки для отправки по sms, включая терминальный ноль.
  const int8_t strSize = sizeMiddle + (strlen(mbStr.call_sign) + strlen(mbStr.loc) + 2) * repeatCounter + 1;
// volatile char str[strSize];
  //
  // Заполняем строку
  str[0] = '\0';
  for (int8_t i = 0; i < repeatCounter; i++) {
    strcat(str, mbStr.call_sign);
    strcat(str, " ");
  }
  strcat(str, middle);
  for (int8_t i = 0; i < repeatCounter; i++) {
    strcat(str, " ");
    strcat(str, mbStr.loc); // Собрали строку текста Маяка
  }
 }
 
// ***** (c) brokly arduino.ru *****
   void sendBeacon(char* _thePhrase) {
   while (*_thePhrase) {
     if (toupper(*_thePhrase)==' '){
        delay(3*DOTLEN);
     } else { for(int8_t i=0;i<sizeof(morseCode)/sizeof(morseCode[0]);i++){
              if (toupper(*_thePhrase) == morseCode[i].letter) {
              uint8_t mask=1;
              while(!(morseCode[i].sequence & mask)){ mask<<=1; }
              mask<<=1;
              while(mask){ (morseCode[i].sequence & mask) ? dot() : dash();  mask<<=1; }
              break; }  }  }
     _thePhrase++;  delay(2*DOTLEN);
   } }
 
// Процедура инициализации текста Маяка (с) UA6EM
   void initBeacon(){
  // MyBeacon mb = {"UA6EaM", "LN14Ae"};  // Данные для Маяка при первом включении 
   eeData = EEPROM.read(eeFlag);
   if (eeData == 73){
   Serial.println(eeData); 
   EEPROM.get(eeAddress, mb);     // получить данные из EEPROM  
   }else{                         // первая запись в EEPROM
   EEPROM.put(eeAddress,mb);
   EEPROM.write(eeFlag, DataFlag);
   }
}


 void setBeacon_Data(){
  my_null=Serial.readString();    // Очистим буфер порта
  modeTime = millis();
  Serial.println("Введите ваш позывной сигнал");
  while(!Serial.available()> 0 && millis() - modeTime <= 10000)  {  }
  if(Serial.available()>0) {
    my_call = Serial.readString();
    my_call.toCharArray(mb.call_sign,20);
  }
    my_call = mb.call_sign;
    Serial.print("Использую ваш позывной сигнал - ");
    Serial.println(my_call);
    
  my_null=Serial.readString();    // Очистим буфер порта
  modeTime = millis();
  Serial.println("Введите ваш QRA локатор");
  while(!Serial.available()> 0 && millis() - modeTime <= 10000)  {  }
  if(Serial.available()>0) {
    my_loc = Serial.readString();
    my_loc.toCharArray(mb.loc,7);
  }
    my_loc = mb.loc;
    Serial.print("Использую QRA локатор - ");
    Serial.println(my_loc);
    EEPROM.put(eeAddress,mb);           // сохраним новые данные в EEPROM
    EEPROM.write(eeFlag, DataFlag); 
    initBeacon();
    sendMsg(mb);
 } // выход из режима программирования


void setup() {
    pinMode(ledPin, OUTPUT);
    pinMode(txPin, OUTPUT);
    Serial.begin(115200);
    initBeacon();
    sendMsg(mb);
    old_millis = millis();
    Serial.println("Для входа в режим программирования введите P");
}
 
void loop() {
 if(millis() - old_millis >= PAUSE){
    digitalWrite(txPin, HIGH);
    delay(900);                    //txdelay - задержка после нажатия PTT
   // sendMsg(mb);                   //текст маяка - проба
    delay(20);
   // sendBeacon(thePhrase);
    sendBeacon(str);
    old_millis = millis();
    digitalWrite(txPin, LOW);
  }
 // Serial.println("Для входа в режим программирования введите P");
  if (Serial.available()) {
    int inByte = Serial.read();
    if(inByte == 'P'){
    setBeacon_Data();
    }
  }
}

Вариант 2

#include <EEPROM.h>
int eeAddress = 0;    //EEPROM address to start reading from
int eeFlag = 73;      // в ячейке 73 сохраняем флаг ==73, что структура сохранялась
int eeData;           // сюда читаем байт из ячейки флага
#define DataFlag 73
 
struct MyBeacon {
char call_sign[20];
char loc[7];
};
MyBeacon mb = {"UA6EM", "LN14AG"}; 
String my_call;
String my_loc;
 
#define SPEED (20)               //скорость в WPM
#define DOTLEN (1200/SPEED)
#define DASHLEN (4*(1200/SPEED))
//#define PAUSE (180000)         //пауза между передачами маяка в милисекундах
#define PAUSE (10000)            //пауза между передачами маяка в милисекундах
unsigned long old_millis;
int txPin=10;       //управление PTT
int ledPin=13;      //мигать встроенным светодиодом или подключить внешний на пин 13
int tonePin=5;      //выход звука
int toneFreq=800;   //Частота звука (выбирайте между 800 - 1500Гц)
 
const int8_t strSize;
volatile char str[50];

//ТАБЛИЦА ГЕНЕРАЦИИ БУКВЫ АЗБУКИ МОРЗЕ
//                    ''    
//                    0         1         2         3         4          5        6         7         8        9
//                    A         B         C         D         E          F        G         H         I        J
//                    K         L         M         N         O          P        Q         R         S        T
//                    U         V         W         X         Y          Z
// описание буквы 0-точка, 1 -тире   
const PROGMEM uint8_t morseCode[]={B00000000, 
                   B00000100,B00001100,B00011100,B00111100,B01111100,B11111100,B11110100,B11100100,B11000100,B10000100,
                   B01100000,B11101000,B10101000,B11010000,B11000000,B10111000,B10010000,B11111000,B11100000,B00011000,
                   B01010000,B11011000,B00100000,B10100000,B00010000,B10011000,B01001000,B10110000,B11110000,B01000000,
                   B01110000,B01111000,B00110000,B01101000,B00101000,B11001000};

// секвенция по символу
uint8_t getMorzeSeq(char in) { 
   uint8_t till = 0;
   if (in < '0') {
      return 0;
   } else if (in <= '9') { 
      till = in - 0x2F; // если цифра , то корректируем на 0x2F;
   } else if (in < 'A') {
      return 0;
   } else if (in <= 'Z') { 
      till = in - 0x36; // заглавные буквы до Z, в нашей таблице с 11 до 37      
   } else if (in < 'a') {
      return 0;
   } else if (in <= 'z') { 
      till = in - 0x56; // маленькие, передаем как заглавные буквы до z, в нашей таблице с 11 до 37   
   }
   if (till>=sizeof(morseCode)) {
      return 0; 
   }
   return pgm_read_byte_near(morseCode+till);
}

//*** передаём тире ***
// (c) Nicola Salsotto IN3GJH
void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }
 
//*** передаём точку ***
// (c) Nicola Salsotto IN3GJH
void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }
 
// *** функция разбора и передачи строки ***
// void sendMsg(char *str){
//  Строка для отправки сформируется внутри,
//  а при выходе будет освобождена
//
  void sendMsg(MyBeacon & mbStr) {
  static const char middle[] = "BEACON QRA is"; // серёдка сообщения
  static const int8_t sizeMiddle = strlen(middle);  // длина серёдки сообщения
  static const int8_t repeatCounter = 2;  // сколько раз повторять позывной и ... тот хвост
  // 
  // strSize - длина строки для отправки по sms, включая терминальный ноль.
  const int8_t strSize = sizeMiddle + (strlen(mbStr.call_sign) + strlen(mbStr.loc) + 2) * repeatCounter + 1;
  //
  // Заполняем строку
  str[0] = '\0';
  for (int8_t i = 0; i < repeatCounter; i++) {
    strcat(str, mbStr.call_sign);
    strcat(str, " ");
  }
  strcat(str, middle);
  for (int8_t i = 0; i < repeatCounter; i++) {
    strcat(str, " ");
    strcat(str, mbStr.loc); // Собрали строку текста Маяка
  }
 }
 

// ***** (c) brokly www.arduino.ru *****
void sendBeacon(char* _thePhrase) {
   while (*_thePhrase) {
     if (*_thePhrase==' '){
        delay(5*DOTLEN);
     } else {
        uint8_t buff=getMorzeSeq(*_thePhrase);
        if(buff){
           uint8_t mask=1;
           while(!(buff & mask)){
              mask<<=1;               
           }
           mask<<=1;
           while(mask){
              (buff & mask) ? dot() : dash();
              mask<<=1;               
           }
           delay(2*DOTLEN);
        }
     }
     _thePhrase++;
    }
}
 
// Процедура инициализации текста Маяка (с) UA6EM
   void initBeacon(){
   eeData = EEPROM.read(eeFlag);
   if (eeData == 73){             // если надо перешить данные в EEPROM из IDE изменить на 74 )))
   Serial.println(eeData); 
   EEPROM.get(eeAddress, mb);     // получить данные из EEPROM  
   }else{
   EEPROM.put(eeAddress, mb);     // сохраним позывной и локатор в EEPROM
   EEPROM.write(eeFlag, eeFlag); }// в ячейку с адресом 73 шьём адрес 73 )))
 }

void setBeacon_Data(){
  Serial.readString();    // Очистим буфер порта
  unsigned long modeTime = millis();
  Serial.println("Введите ваш позывной сигнал");
  while(!Serial.available() && millis() - modeTime < 10000)  {  }
  if(Serial.available()>0) {
    my_call = Serial.readString();
    my_call.toCharArray(mb.call_sign,20);
  }
    my_call = mb.call_sign;
    Serial.print("Использую ваш позывной сигнал - ");
    Serial.println(my_call);
    
  Serial.readString();    // Очистим буфер порта
  modeTime = millis();
  Serial.println("Введите ваш QRA локатор");
  while(!Serial.available() && millis() - modeTime < 10000)  {  }
  if(Serial.available()>0) {
    my_loc = Serial.readString();
    my_loc.toCharArray(mb.loc,7);
  }
    my_loc = mb.loc;
    Serial.print("Использую QRA локатор - ");
    Serial.println(my_loc);
    EEPROM.put(eeAddress,mb);           // сохраним новые данные в EEPROM
    EEPROM.write(eeFlag, DataFlag); 
    initBeacon();
    sendMsg(mb);
 } // выход из режима программирования

 
void setup() {
    pinMode(ledPin, OUTPUT);
    pinMode(txPin, OUTPUT);
    Serial.begin(115200);
    initBeacon();
    sendMsg(mb);
    old_millis = millis();
    Serial.println("Для входа в режим программирования введите P");
}
 
void loop() {
 if(millis() - old_millis >= PAUSE){
    digitalWrite(txPin, HIGH);
    delay(900);                    //txdelay - задержка после нажатия PTT
   // sendMsg(mb);                   //текст маяка - проба
    delay(20);
   // sendBeacon(thePhrase);
    sendBeacon(str);
    old_millis = millis();
    digitalWrite(txPin, LOW);
  }
 // Serial.println("Для входа в режим программирования введите P");
  if (Serial.available()) {
    int inByte = Serial.read();
    if(inByte == 'P'){
    setBeacon_Data();
    }
  }
}

Для варианта 2 можно конечно реализовать и обработку знаков, но код разрастется и экономия в 30 байт в таблице варианта 1 совсем потеряется

negavoid
Offline
Зарегистрирован: 09.07.2016

Деда Витя, это ж не маяк, это ж ключ :)

Вот маяк на 8 МГц:

// Arduino simply CW beacon
// (c) 2012 Max Klimenko
// emaster [at] mail.ru
// http://max-ter.livejournal.com/571.html

// It sends beacon at 8 MHz from pin D9

// Beacon message
const char message[] = "VVV CQ CQ CQ DX DE BEACON BAND 40M 8000KHZ";

// Period of single point (ms)
const int point_time = 80;

// Carrier frequency divider.
// Carrier freq. (MHz) = 16 / (2 * (1 + freq_div))
const unsigned char freq_div = 1;

//////////////////////////////////////////////////////////////////
struct s_abc
{
        char letter;
        char sign[7];
};

const s_abc abc[] = {
        'A',".-", 'B',"-...", 'W',".--", 'G',"--.", 'D',"-..", 
        'E',".", 'V',"...-", 'Z',"--..", 'I',"..",
        'J',".---", 'K',"-.-", 'L',".-..", 'M',"--", 'N',"-.", 
        'O',"---", 'P',".--.", 'R',".-.", 'S',"...",
        'T',"-", 'U',"..-", 'F',"..-.", 'H',"....", 'C',"-.-.", 
        'Q',"--.-", 'Y',"-.--", 'X',"-..-", '1',".----",
        '2',"..---", '3',"...--", '4',"....-", '5',".....", 
        '6',"-....", '7',"--...", '8',"---..", '9',"----.",
        '0',"-----", '.',"......", ',',".-.-.-", ':',"---...", 
        ';',"-.-.-.", '(',"-.--.-", '`',".----.",
        '"',".-..-.", '-',"-....-", '/',"-..-.", '?',"..--..", 
        '!',"--..--", '@',".--.-.", '\\',"..-.-" };

unsigned char abc_size = sizeof(abc) / sizeof(abc[0]);

void setup()
{
  PORTB = 0;
  DDRB |= 1<<1;
  
  OCR1A = freq_div;
  TCCR1A = 0x48;
  TCCR1B = 0x09;
  
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);   // set the LED on
}

void send_letter(char l)
{
  if (l == ' ')
  {
    delay(point_time * 7);
    return;
  }
  
  unsigned char idx = 255;
  for (unsigned char i = 0; i < abc_size; i++)
    if (abc[i].letter == l)
    {
      idx = i;
      break;
    }
    
  if (idx == 255) return;
  
  const char *s = abc[idx].sign;
  
  for (unsigned char c = 0; s[c] != 0; c++)
  {
    char q = s[c];
    
    DDRB |= 1<<1;
    digitalWrite(13, HIGH);   // set the LED on
    
    if (q == '.') delay(point_time);
    else delay(point_time * 3);
    
    DDRB &= ~(1<<1);
    digitalWrite(13, LOW);   // set the LED off
    
    delay(point_time);
  }

  delay(point_time * 2);
}

void loop()
{
  for (int n = 0; message[n] != 0; n++)
    send_letter(message[n]);
  
  delay(2000);
}

А вот маяк на 1337 кГц:

long millisAtStart = 0;
long millisAtEnd = 0;

//period of shortest broadcast (256 port changes)
const long period_broadcast = 8;

// number of period_broadcasts in one 'dit',
// all other lengths are scaled from this
#define LENGTH_DIT 64

const int length_dit = LENGTH_DIT;       //number of periods for dit
const int pause_dit = LENGTH_DIT;        //pause after dit
const int length_dah = 3 * LENGTH_DIT;   //number of persots for dah
const int pause_dah = LENGTH_DIT;        //pause after dah
const int length_pause = 7 * LENGTH_DIT; //pause between words

void dit(void);
void dah(void);
void pause(void);
void broadcast(int N_cycles);
void dontbroadcast(int N_cycles);

// ### INC ### Increment Register (reg = reg + 1)
#define ASM_INC(reg) asm volatile ("inc %0" : "=r" (reg) : "0" (reg))

void setup()
{
  Serial.begin(9600);
  DDRB = 0xFF; //Port B all outputs
  // Do one dit to determine approximate frequency
  millisAtStart = millis();
  dit();
  millisAtEnd = millis();
  Serial.print(millisAtEnd - millisAtStart);
  Serial.print(" ms/dit, freq: ");
  Serial.print( (length_dit + pause_dit) * period_broadcast * 256 / (millisAtEnd - millisAtStart) / 2 );
  Serial.print("kHz ");
  Serial.println();
}

void loop()
{
  dah();
  dit();
  dah();
  dah();
  pause();
  dah();
  dit();
  dah();
  dah();
  pause();
  dah();
  dah();
  dit();
  dit();
  pause();
  pause();
}

void dit(void)
{
  for (int i = 0; i < length_dit; i++)
  {
    broadcast(period_broadcast);
  }
  for (int i = 0; i < pause_dit; i++)
  {
    dontbroadcast(period_broadcast);
  }
}


void dah(void)
{
  for (int i = 0; i < length_dah; i++)
  {
    broadcast(period_broadcast);
  }
  for (int i = 0; i < pause_dah; i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void pause(void)
{
  for (int i = 0; i < length_pause; i++)
  {
    dontbroadcast(period_broadcast);
  }
}

void broadcast(int N_cycles)
{
  unsigned int portvalue;
  for (int i = 0; i < N_cycles; i++)
  {
    portvalue = 0;
    do
    {
      PORTB = portvalue;
      ASM_INC(portvalue);
    }
    while (portvalue < 255);
  }
}

void dontbroadcast(int N_cycles)
{
  unsigned int portvalue;
  PORTB = 0x00;
  for (int i = 0; i < N_cycles; i++)
  {
    portvalue = 0;
    do
    {
      ASM_INC(portvalue);
      //add some assembly No OPerations to keep timing the same
      asm volatile ("NOP");
    }
    while (portvalue < 255);
  }
}

Оба полноценные, никакого больше оборудования, кроме ардуины и куска провода к пину, не требуется.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

negavoid пишет:

Деда Витя, это ж не маяк, это ж ключ :)

ай да Клименко! А, что, красивое решение, это по нашему )))

будет Маяк, а ключ у меня тока разводной и пару-тройку трубных (есть номер 4-ре) )))

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Мда. Похоже скоро китайцы будут говорить, что все что Сделано в России можно сразу на помойку.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Несколько Маяков от UA6HJQ:

1.Простой телеграфный маяк на Arduino

/*
  Простой телеграфный маяк для экспериментов

  Исходный код:
  Written by Nicola Salsotto IN3GJH
  <a data-cke-saved-href="https://github.com/NicoVarg99" href="https://github.com/NicoVarg99" rel="nofollow">https://github.com/NicoVarg99</a>

  Модификация сделана: 
  UA6HJQ 18.11.2018 (добавлено управление радиостанцией)
  UA6EM 21.04.2019 (добавлена функция перевода в верхний регистр, теперь текст можно писать
                    в любом регистре, сделано для дальнешего развития скетча)
  Интервал между передачей:
  10мин= 600000 (передача примерно 20 секунд)
  5мин = 300000
  3мин = 180000
  2мин = 120000
*/

#define SPEED (20)  //скорость в WPM
#define DOTLEN (1200/SPEED)
#define DASHLEN (3*(1200/SPEED))
#define PAUSE (180000)  //пауза между передачами маяка в милисекундах

int txPin=10;       //управление PTT
int ledPin=13;      //мигать встроенным светодиодом или подключить внешний на пин 13
int tonePin=5;      //выход звука
int toneFreq=800;   //Частота звука (выбирайте между 800 - 1500Гц)

void sendMsg(char*);
void dash();
void dot();


void setup()
  {
    pinMode(ledPin, OUTPUT);
    pinMode(txPin, OUTPUT); 
  }


void loop()
  {
    digitalWrite(txPin, HIGH);
    delay(900);    //txdelay - задержка после нажатия PTT
  
      sendMsg("UA6HJQ UA6HJQ BEACON LN14AX"); //текст маяка

    delay(20);
    digitalWrite(txPin, LOW);
  
    delay(PAUSE);
  }


void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void sendMsg(char *str)
{
  int i;

   delay(500);
 
  for(i=0;i<strlen(str);i++)
  {
    switch (toupper(str[i])) // добавлена функция перевода текста в верхний регистр
    {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
    case '.':
      dot();dash();dot();dash();dot();dash();break;
    case ',':
      dash();dash();dot();dot();dash();dash();break;
    case ':':
      dash();dash();dash();dot();dot();break;
    case '?':
      dot();dot();dash();dash();dot();dot();break;
    case '\'':
      dot();dash();dash();dash();dash();dot();break;
    case '-':
      dash();dot();dot();dot();dot();dash();break;
    case '/':
      dash();dot();dot();dash();dot();break;
    case '(':
    case ')':
      dash();dot();dash();dash();dot();dash();break;
    case '\"':
      dot();dash();dot();dot();dash();dot();break;
    case '@':
      dot();dash();dash();dot();dash();dot();break;
    case '=':
      dash();dot();dot();dot();dash();break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;

    }
    delay(2*DOTLEN);
  }

}

 

2. Телеграфный маяк с автоответчиком

CW-BEACON-R7HJ
 

/*
 * UA6HJQ 22.12.2018
 * http://rlsk.ucoz.ru/
 * Радиолюбительский телеграфный маяк с автоответчиком
 * с исправлениями UA6EM от 22.04.2019 скорректирована функция передачи по времени
 *                                     добавлена функция перевода теста для передачи 
 *                                     в верхний регистр (для Маяка)
 *                                     Передачи маяка сразу после включения
 * Управление DTMF кодами: 
 * 1 - версия прошивки
 * 2 - непрерывный тон 1000Гц, длиной 15 секунд
 * 3 - рингтон
 * 4 - температура
 * 5 - напряжение питания
 * 6 - S-метр
 * 7 - передача маяка не дожидаясь очереди
 * 8 - реле включено
 * 9 - реле выключено
 * 0 - текст буквы и цифры
 * * - текст только цифры
 * # - текст только буквы
 * A - не используется...
 * B - не используется...
 * C - не используется...
 * D - не используется...
*/

#include "Config.h" // файл с настройками пользователя
#include "DTMF.h"
#include <OneWire.h>
#include <DallasTemperature.h> 

#define DOTLEN (1200/SPEED)
#define DASHLEN (3*(1200/SPEED))

#define ONE_WIRE_BUS 2 
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

int sensorPin = A0;  // сигнал из эфира
int ledPin=13;       // загорается при приёме DTMF сигнала
int rele1 = 3;       // реле 1
int ptt = A2;        // PTT на корпус во время передачи
int tonePin = A4;    // выход звука на радиостанцию
int smetrPin = A1;   // S-метр, показания RSSI
int smetrReading;

float n = 128.0;
float sampling_rate = 8926.0;    
DTMF dtmf = DTMF(n, sampling_rate);

unsigned long previousMillis;
byte flag_1 = 0;

int nochar_count = 0;
float d_mags[8];

#ifdef PAUSE_GO
byte pause_go = 1;
#else
byte pause_go = 0;
#endif


void setup() 
{
  Serial.begin(115200);
  pinMode(ptt, INPUT);
  digitalWrite(ptt, LOW);
  pinMode(rele1, OUTPUT); 
  pinMode(ledPin, OUTPUT);
  sensors.begin(); // ds18b20
  randomSeed(analogRead(10)); // для генерации случайных чисел указать неиспользуемый порт

  Serial.println("BEACON + Answering Machine\nver. 1.0 ua6hjq@mail.ru 22.12.2018"); 
  Serial.println(MYCALL);
  Serial.println(QTHLOC);
  Serial.print("CW speed "); Serial.print(SPEED); Serial.println(" WPM");
  Serial.print("CW tone "); Serial.print(toneFreq); Serial.println(" Hz");
  Serial.print("Beacon interval ");
  Serial.print(interval);
  Serial.println(" sec");
  sendMsg("E");                                // текст маяка при включении, без передачи в эфир
  previousMillis = millis()- 1000UL*interval;  // устанавливаем время для передачи маяка сразу после включения
}


void loop() 
{
  char thischar;
  dtmf.sample(sensorPin);
  dtmf.detect(d_mags, 506);
  thischar = dtmf.button(d_mags, 1800.);

  if (thischar) {
    Serial.print(thischar);
    nochar_count = 0;

    if (thischar == '1') { Serial.println(" CODE version"); txON(); sendMsg("VER 1 UA6HJQ"); txOFF(); }
      else 
    if (thischar == '2') { Serial.println(" CODE tone 15 sec"); ton(); }
      else 
    if (thischar == '3') { Serial.println(" CODE ring"); ring(); }
      else
    if (thischar == '4') { Serial.print(" CODE temperatura "); temp1(); } 
      else 
    if (thischar == '5') { Serial.println(" CODE beacon"); voltemeter(); }
      else 
    if (thischar == '6') { Serial.print(" CODE "); smetr(); }
      else 
    if (thischar == '7') { Serial.print(" CODE voltemeter "); beacon(); }
      else 
    if (thischar == '8') { Serial.println(" CODE rele on"); digitalWrite(rele1, HIGH); txON(); sendMsg("R"); txOFF(); }
      else 
    if (thischar == '9') { Serial.println(" CODE rele off"); digitalWrite(rele1, LOW); txON(); sendMsg("R"); txOFF(); }
      else 
    if (thischar == '0') { Serial.println(" CODE letters and digital text"); text3(); }
      else
    if (thischar == '*') { Serial.println(" CODE digital text"); text1(); }
      else 
    if (thischar == '#') { Serial.println(" CODE letters text"); text2(); }
      else 
    if (thischar == 'A') { Serial.println(" CODE"); txON(); sendMsg("E"); txOFF(); } 
      else 
    if (thischar == 'B') { Serial.println(" CODE"); txON(); sendMsg("E"); txOFF(); }
      else 
    if (thischar == 'C') { Serial.println(" CODE"); txON(); sendMsg("E"); txOFF(); }
      else 
    if (thischar == 'D') { Serial.println(" CODE"); txON(); sendMsg("E"); txOFF(); }
    }
    
  else {
    if (++nochar_count == 50)Serial.println("");
    if (nochar_count > 30000)nochar_count = 51; 
  }

   if (millis() - previousMillis >= 1000UL*interval){ 
    Serial.println("sec - beacon transmiter");
    if(pause_go){
      previousMillis = millis();    // временные отрезки от начала передачи              
      beacon();
       }else{
      beacon();
      previousMillis = millis();   // временные отрезки от конца передачи    
    }
      } else {} 
} //END

Config.h
 

/*
 * UA6HJQ 25.11.2018
 * ОСНОВНЫЕ НАСТРОЙКИ
 * UA6EM 22.04.2019 отсчет времени от начала/конца передачи
*/

#define MYCALL "UA6EM/B"  // ваш позывной
#define QTHLOC "LN14AE"// ваш локатор

#define SPEED (20)     //скорость передачи телеграфа в WPM

#define toneFreq (800) //частота звука (выбирайте между 600 - 1000Гц)

#define interval (60) // интервал передачи маяка в секундах
                       // 180=3мин 300=5мин 600=10мин  900=15мин  
                       // 1200=20мин  1800=30мин  3600=60мин

#define PAUSE_GO YES   // если надо временные отрезки считать от конца
                       //  передачи Маяка закомментируйте эту строку 
//
// уровень модуляции регулируйте подстроечным резистором на плате!
//

DTMF.cpp
 

/*
Based on: https://github.com/jacobrosenthal/Goertzel
Heavily modified by Pete (El_Supremo on Arduino forums) to decode DTMF tones
It is also public domain and provided on an AS-IS basis. There's no warranty
or guarantee of ANY kind whatsoever.
This uses Digital Pin 4 to allow measurement of the sampling frequency

The Goertzel algorithm is long standing so see
http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
It is often used in DTMF tone detection as an alternative to the Fast
Fourier Transform because it is quick with low overheard because it
is only searching for a single frequency rather than showing the
occurrence of all frequencies.
This work is entirely based on the Kevin Banks code found at
http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
so full credit to him for his generic implementation and breakdown. I've
simply massaged it into an Arduino library. I recommend reading his article
for a full description of whats going on behind the scenes.

Created by Jacob Rosenthal, June 20, 2012.
Released into the public domain.

*/

// include core Wiring API
#include "Arduino.h"

// include this library's description file
#include "DTMF.h"

float SAMPLING_RATE;
float TARGET;
int N;
float coeff[8];
float Q1[8];
float Q2[8];
float cosine;
//PAH int
int testData[160];

const int dtmf_tones[8] = {
  697,
  770,
  852,
  941,
 1209,
 1336,
 1477,
 1633
};

const unsigned char dtmf_map[16] = {
  0x11,
  0x21,
  0x41,
  0x12,
  0x22,
  0x42,
  0x14,
  0x24,
  0x44,
  0x28,
  0x81,
  0x82,
  0x84,
  0x88,
  0x18,
  0x48
};

const char dtmf_char[16] = {
  '1',
  '2',
  '3',
  '4',
  '5',
  '6',
  '7',
  '8',
  '9',
  '0',
  'A',
  'B',
  'C',
  'D',
  '*',
  '#',
};

DTMF::DTMF(float BLOCK)
{
#if F_CPU == 16000000L
DTMF(BLOCK, 8928.0);
#else
DTMF(BLOCK, 4400.0);
#endif
}

DTMF::DTMF(float BLOCK,float SAMPLING_FREQ)
{
  // El_Supremo Set pin 4 as output
  DDRD |= B00010000;
  // Set pin 4 LOW
  PORTD &= B11101111;
    
  SAMPLING_RATE=SAMPLING_FREQ;	//on 16mhz, ~8928.57142857143, on 8mhz ~44444
//>>>  TARGET=TARGET_FREQUENCY; //must be integer of SAMPLING_RATE/N
  N=BLOCK;	//Block size

  float	omega;
  // Calculate the coefficient for each DTMF tone
  for(int i = 0;i < 8;i++) {
    omega = (2.0 * PI * dtmf_tones[i]) / SAMPLING_RATE;
// DTMF detection doesn't need the phase.
// Computation of the magnitudes (which DTMF does need) does not
// require the value of the sin.
// not needed    sine = sin(omega);
    coeff[i] = 2.0 * cos(omega);
  }
  ResetDTMF();
}


/* Call this routine before every "block" (size=N) of samples. */
void DTMF::ResetDTMF(void)
{
  for(int i=0; i<8 ; i++) {
    Q2[i] = 0;
    Q1[i] = 0;
  }
}


/* Call this routine for every sample. */
//El_Supremo - change to int (WHY was it byte??)
void DTMF::ProcessSample(int sample,int adc_centre)
{
  float Q0;
//EL_Supremo subtract adc_centre to offset the sample correctly
  for(int i=0;i < 8;i++) {
    Q0 = coeff[i] * Q1[i] - Q2[i] + (float) (sample - adc_centre);
    Q2[i] = Q1[i];
    Q1[i] = Q0;
  }
}

/* Sample some test data. */
void DTMF::sample(int sensorPin)
{
// El_Supremo
// To toggle the output on digital pin 4
const unsigned char f_counter = 0x10;

  for (int index = 0; index < N; index++)
  {
    testData[index] = analogRead(sensorPin);
// El_Supremo
    // toggle bit 4 for a frequency counter
    PORTD ^= f_counter;    
  }
}

// return the magnitudes of the 8 DTMF frequencies
void DTMF::detect(float dtmf_mag[],int adc_centre)
{
  int index;
  float d_tmp;

  /* Process the samples. */
  for (index = 0; index < N; index++)
  {
    ProcessSample(testData[index],adc_centre);
  }
  // Calculate the magnitude of each tone.
  for(int i=0;i < 8;i++) {
// El_Supremo 150318 the compilers in Arduino verisons 1.6.0 and 1.6.1
// generated "unable to find a register to spill" error in the original statement
// here. Breaking it into pieces worked around the problem.
//     dtmf_mag[i] = sqrt(Q1[i]*Q1[i] + Q2[i]*Q2[i] - coeff[i]*Q1[i]*Q2[i]);

  	// This is the equivalent of sqrt(real*real + imag*imag)
  	d_tmp = Q1[i]*Q1[i];
  	d_tmp += Q2[i]*Q2[i];
  	d_tmp -= coeff[i]*Q1[i]*Q2[i];

    dtmf_mag[i] = sqrt(d_tmp);
  }
  ResetDTMF();
}


char last_dtmf = 0;
// Detect which button was pressed using magnitude as the
// cutoff. Returns the character or a zero
char DTMF::button(float mags[],float magnitude)
{
  int bit = 1;
  int j;
  int dtmf = 0;



  for(int i=0;i<8;i++) {
    if(mags[i] > magnitude) {
      dtmf |= bit;
    }
    bit <<= 1;
  }
  for(j=0;j<16;j++) {
    if(dtmf_map[j] == dtmf)break;
  }
  if(j < 16) {
    // wait for the button to be released
    if(dtmf_char[j] == last_dtmf)return((char) 0);
    last_dtmf = dtmf_char[j];
    return(dtmf_char[j]);
  }
  last_dtmf = 0;
  return((char) 0);
}

DTMF.h
 

/*
Heavily modified by El_Supremo (Arduino forums) to decode DTMF tones
It is also public domain and provided on an AS-IS basis. There's no warranty
or guarantee of ANY kind whatsoever.

The Goertzel algorithm is long standing so see
http://en.wikipedia.org/wiki/Goertzel_algorithm for a full description.
It is often used in DTMF tone detection as an alternative to the Fast
Fourier Transform because it is quick with low overheard because it
is only searching for a single frequency rather than showing the
occurrence of all frequencies.
This work is entirely based on the Kevin Banks code found at
http://www.eetimes.com/design/embedded/4024443/The-Goertzel-Algorithm
so full credit to him for his generic implementation and breakdown. I've
simply massaged it into an Arduino library. I recommend reading his article
for a full description of whats going on behind the scenes.

Created by Jacob Rosenthal, June 20, 2012.
Released into the public domain.
*/

// ensure this library description is only included once
#ifndef DTMF_h
#define DTMF_h

// include types & constants of Wiring core API
#include "Arduino.h"

// library interface description
class DTMF
{
  // user-accessible "public" interface
  public:
    DTMF(float,float);
    DTMF(float);
void sample(int);
void detect(float dtmf_mag[],int adc_centre);
char button(float mags[],float magnitude);
  // library-accessible "private" interface
  private:

//El_Supremo change to int
void ProcessSample(int,int);
void ResetDTMF(void);

};

#endif

cw.ino
 

/* 
 * ФОРМИРОВАНИЕ ТЕЛЕГРАФНОГО СИГНАЛА
 * Written by Nicola Salsotto IN3GJH 
 * https://github.com/NicoVarg99
*/
 
void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void sendMsg(char *str)
{
  int i;

   delay(500);
 
  for(i=0;i<strlen(str);i++)
  {
    switch (toupper(str[i]))
    {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
    case '.':
      dot();dash();dot();dash();dot();dash();break;
    case ',':
      dash();dash();dot();dot();dash();dash();break;
    case ':':
      dash();dash();dash();dot();dot();break;
    case '?':
      dot();dot();dash();dash();dot();dot();break;
    case '\'':
      dot();dash();dash();dash();dash();dot();break;
    case '-':
      dash();dot();dot();dot();dot();dash();break;
    case '/':
      dash();dot();dot();dash();dot();break;
    case '(':
    case ')':
      dash();dot();dash();dash();dot();dash();break;
    case '\"':
      dot();dash();dot();dot();dash();dot();break;
    case '@':
      dot();dash();dash();dot();dash();dot();break;
    case '=':
      dash();dot();dot();dot();dash();break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;

    }
    delay(2*DOTLEN);
  }

}

func.ino
 

/*
 * UA6HJQ 25.11.2018
 * ФУНКЦИИ НЕОБХОДИМЫЕ ДЛЯ РАБОТЫ ВСЕХ РЕЖИМОВ ПРОГРАММЫ
*/

void ring() { 
    txON();
      tone(tonePin, 1000);delay(400);
      tone(tonePin,  800);delay(400);
      tone(tonePin,  600);delay(400);
      tone(tonePin,  400);delay(600);
      noTone(tonePin);
     txOFF();
}


void ton() { 
    txON();
     tone(tonePin, 1000);
     delay(15000);
     noTone(tonePin);
    txOFF();
}


void beacon() { 
    txON();
      sendMsg(MYCALL);
      sendMsg(MYCALL);
      sendMsg("BEACON");
      sendMsg(QTHLOC);
    txOFF();
}

void txON() {
    delay(1000);
    pinMode(ptt, OUTPUT);
    digitalWrite(ptt, LOW);
    delay(600);
}


void txOFF() {
    pinMode(ptt, INPUT);
    digitalWrite(ptt, LOW);
}


void text1() { 
   txON();

    for (int i=0; i <= 9; i++){
     int randNumber = random(10000, 30000);
      char rcw[1];
      itoa(randNumber, rcw, 10); 
      Serial.print(rcw);
      Serial.print(" ");
      sendMsg(rcw);
    }
   
    Serial.println();
   txOFF();
}


void text2() { 
    txON();
     char Str1[ ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    for (int i=0; i <= 9; i++){ 
     for (int s = 1; s <= 5; s++){
      
      int randNumber = random(0, 26);
      Serial.print(Str1[randNumber]);
            
  switch (Str1[randNumber]) {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
     }
      delay(3*DOTLEN);
    }
    Serial.print(" ");
   
    delay(500);
  }
    Serial.println();
   txOFF();
}


void text3() { 
    txON();
     char Str1[ ] = "ABCDEFGHIJ0123456789KLMNOPQRST0123456789UVWXYZ";

    for (int i=0; i <= 9; i++){ 
     for (int s = 1; s <= 5; s++){
      
      int randNumber = random(0, 46);
      Serial.print(Str1[randNumber]);
            
  switch (Str1[randNumber]) {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;
     }
      delay(3*DOTLEN);
    }
    Serial.print(" ");
   
    delay(500);
  }
    Serial.println();
   txOFF();
}


void smetr() {
     smetrReading = analogRead(smetrPin);
     Serial.print("S-metr ");
     Serial.print(smetrReading);
 txON();
         if (smetrReading < 80)  { Serial.println(" = S2"); sendMsg("S 2 2"); } // калибровка уровня S-метра
    else if (smetrReading < 90)  { Serial.println(" = S3"); sendMsg("S 3 3"); } // 55-182 для GM-340 UHF
    else if (smetrReading < 100) { Serial.println(" = S4"); sendMsg("S 4 4"); } 
    else if (smetrReading < 110) { Serial.println(" = S5"); sendMsg("S 5 5"); } 
    else if (smetrReading < 120) { Serial.println(" = S6"); sendMsg("S 6 6"); } 
    else if (smetrReading < 130) { Serial.println(" = S7"); sendMsg("S 7 7"); }
    else if (smetrReading < 140) { Serial.println(" = S8"); sendMsg("S 8 8"); }
                            else { Serial.println(" = S9"); sendMsg("S 9 9"); }
 txOFF();
}


    //вольтметр калибруем в зависимости от резисторов (у меня 15к и 1к)
    // питаем он 5В и проверяем сколько на пине RAW/VCC должно быть примерно столько и в показаниях
    // затем включаем внешнее питание через RAW/VCC должно быть сколько подано

void voltemeter() { //вольтметр на основе делителя из резисторов
    float U1;
    float U2;
    
      float Rs = 1000.0;
      float Rd = 15000.0;
      float Vo = 5.00;
      U2 = Vo * analogRead(A3) / Rs;
      U1 = U2 / ( Rs / ( Rd + Rs ));
      Serial.print(U1);
      Serial.println(" V");
      
      int volt1 = U1;      
      char volt[1];        
      itoa(volt1, volt, 10);
      
    txON(); sendMsg(volt); sendMsg(volt); sendMsg("V"); txOFF();
}
  

void temp1() {
    sensors.requestTemperatures();

    char temp[1];
    itoa(sensors.getTempCByIndex(0), temp, 10);
     Serial.print(temp);
     Serial.println(" C");
    
    txON(); sendMsg(temp); sendMsg(temp); sendMsg("C"); txOFF();
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

negavoid пишет:

Вот маяк на 8 МГц:

Оба полноценные, никакого больше оборудования, кроме ардуины и куска провода к пину, не требуется.

проверил первый, что на 8 мегагерц, CW  сигнал просто великолепный!!!
Слышен на гармониках, на чётных похуже, на нечетных S9++ )))
Вплоть до 144mHz, на 432 нет даже присутствия
То-есть, можно просто поставить умножители, выделить нужные частоты и получить очень простой маячок ...
 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

во.  плод похмельной ночи, отоматический неблокирующий морзе-маяк с таймерами, очередью и блэкджэком. 

https://youtu.be/5O44eD7DJaA

чо скормишь ему, то и будет передавать.  Ннада?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

во.  плод похмельной ночи, отоматический неблокирующий морзе-маяк с таймерами, очередью и блэкджэком. 

https://youtu.be/5O44eD7DJaA

чо скормишь ему, то и будет передавать.  Ннада?

надо

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ок. код причешу, описание набью и выкладу.  В этой теме, или отдельную создать?  тама код абъемный, аж 179 строк. Я помню, ты говорил, что код больше 200 строк ниасилишь, поэтому старался укласца в рамки. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ок. код причешу, описание набью и выкладу.  В этой теме, или отдельную создать?  тама код абъемный, аж 179 строк. Я помню, ты говорил, что код больше 200 строк ниасилишь, поэтому старался укласца в рамки. 

В этой, я буду тут собирать все коды Маяков, что найду

Green
Offline
Зарегистрирован: 01.10.2015

Вы бы сам принцип работы ещё объяснили... И для чего/кого они нужны. Глядишь, люди и потянуться.)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Green пишет:

Вы бы сам принцип работы ещё объяснили... И для чего/кого они нужны. Глядишь, люди и потянуться.)


 

Маяк, устройство подключаемое к радиопередатчику, работающему в автономном режиме, используется радиолюбителями при выезде в полевые условия, служит для точного определения азимута на корреспондента.
Частоты маяков перед выедом как правило озвучиваются на соответствующих сайтах. Так как маяк работает автономно, позволяет оценить прохождение радиоволн в конкретное время суток, отметить точные азимуты на корреспондентов, для последующего использования, в частности в соревнованиях.
Очень помогает при работе на высокочастотных диапазонах (5 - 10 Гигагерц и выше), где угол лепестка диаграммы антенны порядка 1-2 градуса.

Постоянно работающие маяки позволяют оценить прохождение радиосигнала на конкретных корреспондентов.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Попробовал маяк Клименко на WAVGAT nano, от внутреннего генератора сигнал просто ужасен, от внешнего кварца, а он на плате 12 мегацерцовый сигнал приемлемый, на атмеге ардуино нано всё же получше, там он кристально чистый.
Хочу попробовать сменить кварц на 28 мгц, тогда маяк будет работать на частоте 14 мегагерц и кратные этой частоте.
Да, уровень сигнала значительно ниже, сказывается питание  камня от 3.3 вольта )))
Скетч здесь, аддоны брал из этой ветки форума

// Arduino simply CW beacon
// (c) 2012 Max Klimenko
// emaster [at] mail.ru
// <a href="http://max-ter.livejournal.com/571.html" title="http://max-ter.livejournal.com/571.html" rel="nofollow">http://max-ter.livejournal.com/571.html</a>

// It sends beacon at 8 MHz from pin D9
#include "lgtx8p.h"

// Beacon message
const char message[] = "VVV CQ CQ CQ DX DE BEACON BAND 40M 8000KHZ";

// Period of single point (ms)
const int point_time = 80;

// Carrier frequency divider.
// Carrier freq. (MHz) = 16 / (2 * (1 + freq_div))
const unsigned char freq_div = 1;

//////////////////////////////////////////////////////////////////
struct s_abc
{
        char letter;
        char sign[7];
};

const s_abc abc[] = {
        'A',".-", 'B',"-...", 'W',".--", 'G',"--.", 'D',"-..", 
        'E',".", 'V',"...-", 'Z',"--..", 'I',"..",
        'J',".---", 'K',"-.-", 'L',".-..", 'M',"--", 'N',"-.", 
        'O',"---", 'P',".--.", 'R',".-.", 'S',"...",
        'T',"-", 'U',"..-", 'F',"..-.", 'H',"....", 'C',"-.-.", 
        'Q',"--.-", 'Y',"-.--", 'X',"-..-", '1',".----",
        '2',"..---", '3',"...--", '4',"....-", '5',".....", 
        '6',"-....", '7',"--...", '8',"---..", '9',"----.",
        '0',"-----", '.',"......", ',',".-.-.-", ':',"---...", 
        ';',"-.-.-.", '(',"-.--.-", '`',".----.",
        '"',".-..-.", '-',"-....-", '/',"-..-.", '?',"..--..", 
        '!',"--..--", '@',".--.-.", '\\',"..-.-" };

unsigned char abc_size = sizeof(abc) / sizeof(abc[0]);

void setup()
{
  // Затактироваться от OSC на плате, при 12 мГц кварце (частота около 6мгц)
 /*
  sysClock(EXT_OSC);
  CLKPR = 1<<PMCE;//разрешить изменение 
  CLKPR = 1<<5; // вывод clk
  */
  
// Затактироваться от внутреннего генератора 32мГц (частота маяка 16 мегагерц)
  CLKPR = 1<<PMCE;//разрешить изменение
  CLKPR = B00000000;

  // Затактировать от внешнего генератора 32мГц
/*  int main(){
PMCR=1<<PMCE; //разрешить выбор источника тактирования
PMCR= 1<<2 | 1<<5; //External high frequency crystal 
PMX2= 1<<WCE;//разрешить изменения
PMX2= 1<<XIEN;//разрешить вход тактовой частоты от кварц. генератора
CLKPR = 1<<PMCE;//разрешить изменение
CLKPR = 1<<5; //делитель =1 и вывод clk
} */

  PORTB = 0;
  DDRB |= 1<<1;
  
  OCR1A = freq_div;
  TCCR1A = 0x48;
  TCCR1B = 0x09;
  
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);   // set the LED on
}

void send_letter(char l)
{
  if (l == ' ')
  {
    delay(point_time * 7);
    return;
  }
  
  unsigned char idx = 255;
  for (unsigned char i = 0; i < abc_size; i++)
    if (abc[i].letter == l)
    {
      idx = i;
      break;
    }
    
  if (idx == 255) return;
  
  const char *s = abc[idx].sign;
  
  for (unsigned char c = 0; s[c] != 0; c++)
  {
    char q = s[c];
    
    DDRB |= 1<<1;
    digitalWrite(13, HIGH);   // set the LED on
    
    if (q == '.') delay(point_time);
    else delay(point_time * 3);
    
    DDRB &= ~(1<<1);
    digitalWrite(13, LOW);   // set the LED off
    
    delay(point_time);
  }

  delay(point_time * 2);
}

void loop()
{
  for (int n = 0; message[n] != 0; n++)
    send_letter(message[n]);
  
  delay(2000);
}

 

-NMi-
Offline
Зарегистрирован: 20.08.2018

У Вас там в Черкесске ещё радиогубители осталися???

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

DetSimen пишет:

ок. код причешу, описание набью и выкладу.  

Ан нет, быстро не выкладу.  Там, аказываеца, думать надо.  Крепко думать надо, и даже трезвым, а то хрень с налёту получица. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

-NMi- пишет:

У Вас там в Черкесске ещё радиогубители осталися???

 а то )))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ua6em, ты мне, кста, можешь существенно помочь, если набьешь пока недостающее. 

const TMorze MorzeArray[] PROGMEM = {
	{'A',0b101},
	{'B',0b11000},
	{'C',0b11010},
	{'D',0b1100},
	{'E',0b10},
//  .
	{'K',0b1101},
//  .
	{'S',0b1000},
//  .
//  .
	{'Q',0b11101},
//  .
	{'Z',0b11100},
	{'1',0b101111},
	{'2',0b100111},
	{'3',0b100011 },

	//  .
	{'9',0b111110},
	{'0',0b111111},
//  .
//  .
	{'?',0b1001100}
};

символ и код морзе в бинарном виде (0bxxxx), для кода морзе  правило простое, ведущая единица (мы ее пропустим потом), а потом сам код морзя, 1-тире, 0- точка 

например 'Щ' ('Q') ..--..  = 0b1 001100  - пробелом я отделил ведущую единицу от собственно кода Морзе, в реальном коде пробел ставить не нужно

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

символ и код морзе в бинарном виде (0bxxxx), для кода морзе  правило простое, ведущая единица (мы ее пропустим потом), а потом сам код морзя, 1-тире, 0- точка 

например 'Щ' ('Q') ..--..  = 0b1 001100  - пробелом я отделил ведущую единицу от собственно кода Морзе, в реальном коде пробел ставить не нужно

 

сейчас на работу доеду и набью, точнее в течении часа сделаю

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

дак я не тороплю же, у мня часть есть, отлаживать можно и на этом.  Потом, как сделаешь, подставим, и решение будет полным.  С кириллицей потом чонить придумаем, мошт таблицу перекодировки какую. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016
const TMorze MorzeArray[] PROGMEM = {
  {'A',0b101},
  {'B',0b11000},
  {'C',0b11010},
  {'D',0b1100},
  {'E',0b10},
  {'F',0b10010},
  {'G',0b1110},
  {'H',0b10000},
  {'I',0b100}, 
  {'J',0b10111},  
  {'K',0b1101},
  {'L',0b10100}, 
  {'M',0b111}, 
  {'N',0b110},  
  {'O',0b1111}, 
  {'P',0b10110}, 
  {'Q',0b11101},
  {'R',0b1010},          
  {'S',0b1000},
  {'T',0b11}, 
  {'U',0b1001}, 
  {'V',0b10001}, 
  {'W',0b1011}, 
  {'X',0b11001}, 
  {'Y',0b11011}, 
  {'Z',0b11100},
  {'1',0b101111},
  {'2',0b100111},
  {'3',0b100011},
  {'4',0b100001},
  {'5',0b100000},
  {'6',0b110000},
  {'7',0b111000},
  {'8',0b111100},  
  {'9',0b111110},
  {'0',0b111111},
  {'.',0b1010101},
  {',',0b1110011},  
  {':',0b1111000},  
  {'\\',0b1011110}, 
  {'-',0b1100001}, 
  {'/',0b110010}, 
  {'(',0b1101101},
  {')',0b1101101},  
  {'@',0b1011010},  
  {'=',0b110001}, 
  {'"',0b1010010}, 
  {''',0b1011110},                   
  {'?',0b1001100}
  {'!',0b1110011}
  {';',0b1101010}

};

Если ничего не пропустил )))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Спасибо, забрал

Восклецательный знак { '!',0b1110011 } забыл. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Как там с причёсыванием?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Жди. Или вдумчиво и надежно, или быстро.  Хочешь, завтра выложу? 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

не думаю, что еслиф ты без маяка жил 50 лет, то не проживёшь еще 3-4 дня. 

Жди. я же кроме писания программ от нечего делать, еще и таки немношко пью.  :) 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

не думаю, что еслиф ты без маяка жил 50 лет, то не проживёшь еще 3-4 дня. 

Жди. я же кроме писания программ от нечего делать, еще и таки немношко пью.  :) 

это другое дело ... да маяк в принципе работает, вот тут скетчик от UA6HJQ немного поправил, у него интересная задумка...

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Гы. Очень сильно немношко.  :-)

Именно поэтому я не беру заказов. Сегодня я здесь, а завтра в запое, и чем закащик виноват в такой ситуации?  А такак у мня остались еще жалкие фрагменты совести, то мне неудобно подставлять непричастных людей.  Тебе я ничего, сопсно, и не обещал, мне стала интересна задача, сиравно заняца нечем пока, я попробую сделать. Но уж, как смогу. Не получица - не обессудь, голова моя уже как в децтве работать не в состояньи. :-) 

Можешь кидать в мня тапками, я ниабижусь. :-)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Моя задумка готова пока на 60%. 

Я очень сильно сожалею, если вдруг обидел каво. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Тебе строку откуда брать?  Из сериала или жоска заданную в тексте?  

Пока сделал чтение из Сериала, что туда напишешь, то и будет по кругу передавать.  Напишешь другое - через 2 секунды будет другое передавать. 

В принципе, в АТМега8 код влазиит, даже, наерна, тиньку приспособить можно будет

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

мне интересен сам твой алгоритм, лучше из сериала, тогда можно клавиатуру притарачить, да я сам всё делаю из интереса )))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Ок.  Сериал оставлю.  Набираешь в сериале нужный текст.  Отправляешь.  когда прошло 2 секунды с момента приема последнего символа - передаваемая строка заменяется принятой из сериала и сразу начинает передаваца

Алгоритм простой, на таймерах и флагах.  К вечеру выложу на гитхаб.  Наерна. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

  К вечеру выложу на гитхаб.  Наерна. 

Хорошо!

А я тут небольшой тренер (по мотивам R7HJ) за это время насобирал, пока не оптимизировал, компилируется )))
 

/*
 * 24.04.2019 (с) UA6EM <a href="mailto:ua6em@yandex.ru">ua6em@yandex.ru</a>, cтарт проекта
 * 
 * Предлагается скетч простого телеграфного датчика кода Морзе.
 * Для соревнований тексты формируются в трёх режимах: Цифры, Буквы, 
 * Смешанные тексты, время передачи радиограммы составляет 1 минуту 
 * с выравниваем текста до полной группы.
 * Скорость передачи регулируется с шагом 10 знаков в минуту.
 * 
 * 
 */
#define DOTLEN (1200/SPEED)
#define DASHLEN (3*(1200/SPEED))
#define SEC 1000UL
#define SPEED (60)     //скорость передачи телеграфа в WPM
#define toneFreq (800) //частота звука (выбирайте между 600 - 1000Гц)

byte test_go = 1;
byte test_mode = 1;
int ledPin=13;  
int tonePin = A4;    // выход звука
unsigned long previousMillis; 
unsigned long test_time = 60; // время теста одна минута

 
void setup() {
  Serial.begin(115200);
  previousMillis =  millis();
}

void loop() {
  
  if(test_go){
    while(millis() - previousMillis <= test_time * SEC){
      if(test_mode == 1) { Serial.println();cw_digi();}
      if(test_mode == 2) { Serial.println();cw_char();}
      if(test_mode == 3) { Serial.println();cw_chdg();}
    }
    delay(10000);
    previousMillis =  millis();
    test_mode++;
    if(test_mode == 4) test_mode=1;
  }
}

И ...при нём
 

/*
 * Библиотеки для CW 
 */

//
// *** Генерация цифровых кодов ***

 void cw_digi() { 
  
      for (int i=0; i <= 9; i++){
      int randNumber = random(10000, 30000);
      char rcw[1];
      itoa(randNumber, rcw, 10); 
      Serial.print(rcw);
      Serial.print(" ");
      sendMsg(rcw);
    }
   
    Serial.println();
  }


//
// *** Генерация символьных кодов ***  
 
 void cw_char() { 

     char Str1[ ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    for (int i=0; i <= 9; i++){ 
     for (int s = 1; s <= 5; s++){
      
      int randNumber = random(0, 26);
      Serial.print(Str1[randNumber]);
            
  switch (Str1[randNumber]) {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
     }
      delay(3*DOTLEN);
    }
    Serial.print(" ");
   
    delay(500);
  }
    Serial.println();
 }


//
// *** Генерация смешанного текста ***  
void cw_chdg() { 
     char Str1[ ] = "ABCDEFGHIJ0123456789KLMNOPQRST0123456789UVWXYZ";

    for (int i=0; i <= 9; i++){ 
     for (int s = 1; s <= 5; s++){
      
      int randNumber = random(0, 46);
      Serial.print(Str1[randNumber]);
            
  switch (Str1[randNumber]) {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;
     }
      delay(3*DOTLEN);
    }
    Serial.print(" ");
   
    delay(500);
  }
    Serial.println();
}


void dash()
  {
    digitalWrite(ledPin, HIGH);
    tone(tonePin, toneFreq);
    delay(DASHLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void dot()
  {
    digitalWrite(ledPin, HIGH) ;
    tone(tonePin, toneFreq);
    delay(DOTLEN);
    digitalWrite(ledPin, LOW);
    noTone(tonePin);
    delay(DOTLEN);
  }


void sendMsg(char *str)
{
  int i;

   delay(500);
 
  for(i=0;i<strlen(str);i++)
  {
    switch (toupper(str[i]))
    {
    case 'A':
      dot();dash();break;
    case 'B':
      dash();dot();dot();dot();break;
    case 'C':
      dash();dot();dash();dot();break;
    case 'D':
      dash();dot();dot();break;
    case 'E':
      dot();break;
    case 'F':
      dot();dot();dash();dot();break;
    case 'G':
      dash();dash();dot();break;
    case 'H':
      dot();dot();dot();dot();break;
    case 'I':
      dot();dot();break;
    case 'J':
      dot();dash();dash();dash();break;
    case 'K':
      dash();dot();dash();break;
    case 'L':
      dot();dash();dot();dot();break;
    case 'M':
      dash();dash();break;
    case 'N':
      dash();dot();break;
    case 'O':
      dash();dash();dash();break;
    case 'P':
      dot();dash();dash();dot();break;
    case 'Q':
      dash();dash();dot();dash();break;
    case 'R':
      dot();dash();dot();break;
    case 'S':
      dot();dot();dot();break;
    case 'T':
      dash();break;
    case 'U':
      dot();dot();dash();break;
    case 'V':
      dot();dot();dot();dash();break;
    case 'W':
      dot();dash();dash();break;
    case 'X':
      dash();dot();dot();dash();break;
    case 'Y':
      dash();dot();dash();dash();break;
    case 'Z':
      dash();dash();dot();dot();break;
    case ' ':
      delay(DOTLEN*5);break;
    case '.':
      dot();dash();dot();dash();dot();dash();break;
    case ',':
      dash();dash();dot();dot();dash();dash();break;
    case ':':
      dash();dash();dash();dot();dot();break;
    case '?':
      dot();dot();dash();dash();dot();dot();break;
    case '\'':
      dot();dash();dash();dash();dash();dot();break;
    case '-':
      dash();dot();dot();dot();dot();dash();break;
    case '/':
      dash();dot();dot();dash();dot();break;
    case '(':
    case ')':
      dash();dot();dash();dash();dot();dash();break;
    case '\"':
      dot();dash();dot();dot();dash();dot();break;
    case '@':
      dot();dash();dash();dot();dash();dot();break;
    case '=':
      dash();dot();dot();dot();dash();break;
    case '0':
     dash();dash();dash();dash();dash();break;
    case '1':
     dot();dash();dash();dash();dash();break;
    case '2':
     dot();dot();dash();dash();dash();break;
    case '3':
     dot();dot();dot();dash();dash();break;
    case '4':
     dot();dot();dot();dot();dash();break;
    case '5':
     dot();dot();dot();dot();dot();break;
    case '6':
     dash();dot();dot();dot();dot();break;
    case '7':
     dash();dash();dot();dot();dot();break;
    case '8':
     dash();dash();dash();dot();dot();break;
    case '9':
     dash();dash();dash();dash();dot();break;

    }
    delay(2*DOTLEN);
  }

}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

тваюштымать.  ну как не надоест то ватэтовотвсё

291     case '?':
292       dot();dot();dash();dash();dot();dot();break;
293     case '\'':
294       dot();dash();dash();dash();dash();dot();break;
295     case '-':
296       dash();dot();dot();dot();dot();dash();break;
 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нет. Сегодня вряд ли успею. Жучков много. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

Нет. Сегодня вряд ли успею. Жучков много. 

Да не торопись! Я дед только вникать в твой код неделю буду...PS Скидывал там скетч вверху, две ошибки на 50 строк, лохматость уже совсем не та ...ЗЫ и это не синтаксис, можно было сослаться на незнание языка, а логика...)))
 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

здоброй ночей. 

Значить, берешь, лезешь вот сюда. 

https://github.com/DetSimen/MorzeRepeater

скачиваешь оттудова Zip файл 

В Zip файле есть директория DtS, просто скопируй всё ее содержимое к себе в %Arduino%\libraries\DtS. ПОСЛЕ ЭТОГО только запускай IDE и открывай Morze.ino.  Канпилируй, матерись, спрашивай, наливай. 

Для вопросов и обсуждения, воть файл Morze.ino (осторожна!!! Dead-o-code): 

#include "Arduino.h"
#include "MorzeTable.h"
#include "TimerList.h"
#include "Messages.h"

static uint8_t TXPin = 10;  // пин передаччика

extern TTimerList TimerList;	// списой таймеров (до 10) паумолчанью

TMessageList MessageList(12);  // очереть на 12 сапщений

static const int msg_ReadSerial = 0x100;   // сообщение "Читать Сериал"
static const int msg_SendChar = 0x101;		// сообщение "Передать символ (букву)"
static const int msg_SendNextBit = 0x102;	// сообщение "Передать следующий бит в букве"

static const uint32_t DOT_TIME = 75;  // основное время, длительность точки 75 мс, остальные производные
static const uint32_t DASH_TIME = 3 * DOT_TIME; // длительность тире

static const uint16_t REPEAT_TIME = 5000;  // Задержка в мс между повторениями текста, по умолчанию 5 сек. 
static const uint16_t SERIAL_TIMEOUT = 2000; // если в течение 2000 мс не было данных с сериала, значит приняли всю строку

static const uint8_t MAX_STRING_LENGTH = 128; // макс длина передаваемой строки

enum class enumTXState : bool { Pause = false, Bit = true };  

enumTXState TXState = enumTXState::Pause;  // что передаем в данный момент: или бит (точка/тире) или паузу

THandle hTXTimer = INVALID_HANDLE;       // таймер передаччика
THandle hSerialTimer = INVALID_HANDLE;	// таймер таймаута приема из Serial
THandle hRepeatTimer = INVALID_HANDLE;	// таймер повтора фразы

uint8_t TXCurrentMask = 0;   // маска текущего символа
uint8_t TXCurrentCode = 0;   // битовый код Морзе текущего символа
bool	TXBusy = false;		//  идет передача бита или паузы
bool	TXStopped = false;  //  передаччик остановлен нахрен. ничего не передаеца

String StringToTransmit;            // строка, которую будем слать 
uint16_t TransmitCharIndex = 0;		// индекс текущего символа, в этой строке

void TXOnOff(const bool On) {    // передаччик вкл/выкл
	digitalWrite(TXPin, On);
}

void tmrTXTimer(void) {     // здесь кончился таймер передачи бита/паузы
	SendMessage(msg_TimerEnd, hTXTimer);
	TimerList.Stop(hTXTimer); // остановим этот таймер 
}

void tmrSerialTimer(void) {  // строку приняли до конца
	SendMessage(msg_TimerEnd, hSerialTimer);
	TimerList.Stop(hSerialTimer); // таймер больше не нужен
}

void tmrRepeat(void) {     // таймер повтора кончился, начинаем передавать сначала
	TransmitCharIndex = 0;
	TXStopped = false;          
	TimerList.Stop(hRepeatTimer);
}

void sendBit(const bool aBit) {  // передать один бит (точку == false или тире == true)

	TXState = enumTXState::Bit;  // признак: передаем бит

	TXOnOff(true);				// ключ на старт!  

	TXBusy = true;				// передаччик теперя занят

	TimerList.setNewInterval(hTXTimer, aBit ? DASH_TIME : DOT_TIME);  // длительность таймера разная для точки и тире
	TimerList.Reset(hTXTimer);  // перезапустим его сначала интервала

}

void sendPause(const uint8_t kf = 1) {  // передаем паузу длиной в 1 точку по умолчанию
	TXState = enumTXState::Pause;
	
	TXBusy = true; // передаччик занят
	
	TimerList.setNewInterval(hTXTimer, kf*DOT_TIME);  // либо длительность паузы кратна точке в kf раз
	TimerList.Reset(hTXTimer);  // перезапустим таймер сначала
}

; void setup()
{
	Serial.begin(115200);

	delay(1000); // чтоб всё устаканилось к старту

	pinMode(TXPin, OUTPUT);

	TXOnOff(false);	// ключ выключен

	// все таймеры создаюца по умолчанию остановленными
	//
	hTXTimer = TimerList.AddStopped(DOT_TIME, tmrTXTimer);  // таймер передаччика бит 
	hSerialTimer = TimerList.AddStopped(SERIAL_TIMEOUT, tmrSerialTimer);  // таймер таймаута чтения из Serial  
	hRepeatTimer = TimerList.AddStopped(REPEAT_TIME, tmrRepeat);  // таймер повтора передачи строки 

	StringToTransmit.reserve(MAX_STRING_LENGTH); // хапнем сразу место под строку, чтоб не перераспределять потом
	StringToTransmit = "CQ DX UA6EM CQ DX UA6EM QSA? 73! 73! 73! K";  // строка для передачи по умолчанию
	TransmitCharIndex = 0;  
}

void loop()
{
	if (Serial.available()) {        // если в сериал чота припрыгало
		SendMessage(msg_ReadSerial);	// пошлем команду прочесть сериал
		TXStopped = true;				// передачу остановим
		TransmitCharIndex = 0;		// и все индексы и маски обнулим, 
		TXCurrentCode = 0;			// так как после приема будем передавать уже новую строку
		TXCurrentMask = 0;
	}

	if ((not TXBusy) and (not TXStopped)) {  // если передаччик не остановлен и не занят передачей бита

		if (TXCurrentMask > 0)  // если маска еще до конца не сдвинулась
		{
			SendMessage(msg_SendNextBit); // передать следующий бит знака
		}
		else 
		{
			SendMessage(msg_SendChar); // знак кончился, начать передавать след. символ
		}
	}


	if ( not MessageList.Available()) return;   // если сапщений в очереди нет, выходим

	TMessage msg = MessageList.GetMessage();

	switch (msg.Message)
	{

	case msg_TimerEnd: {
		if (msg.LoParam == hSerialTimer) {  // кончился таймер приёма по сериал
			TXStopped = false;		// можно стартовать передаччик, есличо
			break;
		}
		if (msg.LoParam == hTXTimer) { // кончился таймер передачи бита/паузы
			TXBusy = false;			// можно передавать следующий

			if (TXState == enumTXState::Bit) { // если передавали бит, передадим 1 паузу
				TXOnOff(false);					// выключим ключ
				TXCurrentMask >>= 1;
				if (TXCurrentMask>0) sendPause(); else sendPause(3); // а если знак кончился, то 3 паузы
			}

			break;
		}

		break;
	}

	case msg_SendNextBit: {   // передаем след. бит  (точка/тире) 
		sendBit(TXCurrentCode & TXCurrentMask);
		break;
	}

	case msg_ReadSerial: {   // чтение из сериала
		if (!TimerList.isActive(hSerialTimer)) {  // если таймер таймаута остановлен 
			StringToTransmit = "";					// значить читаем новую строку
			TransmitCharIndex = 0;
		}
		StringToTransmit += char(Serial.read()); // берем символ из сериал и вклеиваем в строку
		TimerList.Reset(hSerialTimer);			// перезапускаем таймер таймаута (помнишь? 2 секунды после ПОСЛЕДНЕГО символа) 
		break;
	}

 	case msg_SendChar: {    // послать знак
		if (TransmitCharIndex < StringToTransmit.length()) { // если строка еще не кончиилась
			char c = StringToTransmit[TransmitCharIndex++]; // взять символ 
			if (c != ' ') {  // если не пробел, то
				TXCurrentCode = getMorzeCode(c);				// по нему взять код Морзя  см. MorzeTable.cpp
				TXCurrentMask = getSymbolMask(getMorzeCode(c)); // и маску    см. MorzeTable.cpp
			}
			else sendPause(7);  // пробел между словами - передаем паузу в 7 точек
		}
		else {    // если строка кончилась
			TXStopped = true;			// остановим передачу
			TransmitCharIndex = 0;			// вернем указатель на 0 символ строки
			TimerList.Reset(hRepeatTimer);	// и запустим таймер для REPEAT
		}
		break;
	}

	default:
		Serial.print(F("Unknown message code: 0x")); Serial.println(msg.Message, HEX);
		break;
	}

}

К 10 пину подключи светлодиот/бузер/ключ на полевике/Братскую ГЭС, всё чо хочешь, короче. 

При старте начинает "передавать" фразу по умолчанию.  Чтоб сменить, надо в Сериал отправить всё, что надо передавать.  Через 2 секунды после приема ПОСЛЕДНЕГО символа, принятая фраза начинает передаваца.  После окочания передачи всей фразы - пауза 5 секунд и передача сначала.  Все тайминги настраиваюца в самом начале. Комментарии тоже есть.  Разбирайся. Матерись. Спрашивай. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

код морзе берется из таблицы в прогмем.  Как получается код в бинарном виде: 

Допустим, закодируем знак вопроса ..--.. 

ставим 1, это флаг, что после нее до конца байта будут значащие биты, 0-точка, 1-тире

0b1001100

из этого кода, пропусканием всех нулевых бит от начала байта и флага получаем маску символа 

0b1001100  символ

0b0100000 - его маска, единичка стоит в первом значащем разряде

делаем маске и коду битовый AND, получаем 0 (точка) или не ноль (тире)

передаем этот бит

маску сдвигаем вправо на бит

пока маска не 0, т.е не сдвинулась до конца, передаем очередной бит.

символ 0b1001100

маски   0b0100000

            0b0010000

            0b0001000

             .

            0b0000001

и всё, символ кончился. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

хорошо!
СКОМПИЛИРОВАЛ - ИЗУЧАЮ

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

И чо?  скомпилировалось намайна?  

у мня для Уно 

Program size: 6 330 bytes (used 20% of a 32 256 byte maximum) (1,69 secs)
Minimum Memory Usage: 363 bytes (18% of a 2048 byte maximum)
 
светлодиот на 10 пин подключил?  Мигаеть?
 
 
 
Я сегодня где-то в 13-00 по вашему сдрисну внадачу, там мне неудобно будет тыкать в планшет.  Поэтому если чо, спрашивай лучше щас. 
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ВСЁ НОРМАЛЬНО! у нас еще рано, хочу зуммер подключить послушать, разбужу тут всех )))

Работает!!!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Перезалил, уклался в 200 строк. Выкинул лишние сучности

теперь карикатура занимает 5272 байта PROGMEM и 280 АЗУ

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Ой.  Забыл про саафтара.  Подтверждаю, понужал меня не спать изо всех сил. 

Green
Offline
Зарегистрирован: 01.10.2015

ua6em, а я всё хотел спросить. Вот ваш маяк, подключили вы его к своему ТХ. Как я понимаю, сами при этом работать не можете - ведь маяк за вас работает... Или есть какие то иные правила?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Это ж как на рыбалке: приехал, удочки поставил, налил... Ну, иногда можно и дёрнуть, когда закусывать надоело ))

Green
Offline
Зарегистрирован: 01.10.2015

Нет, для своего позывного нужно же иметь разрешение с соответствующей категорией и пр. А так получается что вместо тебя работает робот и возможна ситуация когда твоих экземпляров класса может быть несколько.) С одинаковыми позывными! Что как то странненько.)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

синглтон, аднака, нужен

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Green пишет:

Нет, для своего позывного нужно же иметь разрешение с соответствующей категорией и пр. А так получается что вместо тебя работает робот и возможна ситуация когда твоих экземпляров класса может быть несколько.) С одинаковыми позывными! Что как то странненько.)

РосЭфирЗапрет поймает роботов и разберётся, как полагается. Около робота же человек сидит обумаженный - следит, стало быть, за бездушной железякой. А этак ведь и обычный алко... партизан в тылу врага может отстучать под чужой фамилией - разницы нет ведь.

 

Green
Offline
Зарегистрирован: 01.10.2015

Не, ну это понятно. Хулиганить может каждый, только всё это ведь не законно. А мы же ж законопослушные граждане.)
Хотя раньше что бы выполнить норматив приходилось идти на обман. ЕМНИП, была категория один передатчик - несколько операторов, а категории несколько передатчиков - несколько опрераторов небыло. Однако, многие работали именно так, потому как конкуренция была большая.) 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Законопослушные граждане по десять роботов не пускают в эфир под одним аккаунтом. Пока рука стакан держит - робот стучит. Освободилась рука - батарейку у робота отобрал и сам трели выдаёшь. Так что суть вопроса до сих пор неясна.

Green
Offline
Зарегистрирован: 01.10.2015

Ну вот и я о том же. Можно ли десяток роботов пускать под одним аккаунтом? Та даже одного робота и себя?