├── TelePager-bot ├── .gradle │ ├── 7.1.1 │ │ ├── gc.properties │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── dependencies-accessors │ │ │ ├── gc.properties │ │ │ └── dependencies-accessors.lock │ │ ├── fileHashes │ │ │ ├── fileHashes.bin │ │ │ └── fileHashes.lock │ │ └── executionHistory │ │ │ ├── executionHistory.bin │ │ │ └── executionHistory.lock │ ├── vcs-1 │ │ └── gc.properties │ ├── buildOutputCleanup │ │ ├── cache.properties │ │ ├── outputFiles.bin │ │ └── buildOutputCleanup.lock │ └── checksums │ │ ├── checksums.lock │ │ ├── md5-checksums.bin │ │ └── sha1-checksums.bin ├── settings.gradle ├── .idea │ ├── .gitignore │ ├── compiler.xml │ ├── misc.xml │ ├── gradle.xml │ ├── jarRepositories.xml │ └── uiDesigner.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle ├── src │ └── main │ │ └── java │ │ ├── Main.java │ │ ├── PagerBot.java │ │ └── Serial.java ├── gradlew.bat └── gradlew ├── NotificationsToPager-android ├── app │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── themes.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── values-night │ │ │ │ │ └── themes.xml │ │ │ │ ├── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── layout │ │ │ │ │ └── activity_main.xml │ │ │ │ └── drawable │ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── ru │ │ │ │ └── example │ │ │ │ └── notificationstopager │ │ │ │ ├── NotificationService.kt │ │ │ │ ├── Translit.java │ │ │ │ └── MainActivity.kt │ │ ├── test │ │ │ └── java │ │ │ │ └── ru │ │ │ │ └── example │ │ │ │ └── notificationstopager │ │ │ │ └── ExampleUnitTest.java │ │ └── androidTest │ │ │ └── java │ │ │ └── ru │ │ │ └── example │ │ │ └── notificationstopager │ │ │ └── ExampleInstrumentedTest.java │ ├── proguard-rules.pro │ └── build.gradle ├── .idea │ ├── .name │ ├── .gitignore │ ├── compiler.xml │ ├── misc.xml │ └── gradle.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── previ.jpg ├── Schematic.jpg ├── Variant by Dmitry Stupak ├── Schematic.jpg └── UniversalPagerTransmiter.lay6 ├── UneversalPagerSender-arduino ├── MemoryFree.h ├── MemoryFree.cpp ├── CmdProc.h ├── Storage.cpp ├── Storage.h ├── Cmd.h ├── Rf7021.h ├── PocsagEncoder.h ├── Cmd.cpp ├── PocsagEncoder.cpp ├── Rf7021.cpp ├── CmdProc.cpp └── UneversalPagerSender.ino └── README.md /TelePager-bot/.gradle/7.1.1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TelePager-bot/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NotificationsToPager-android/.idea/.name: -------------------------------------------------------------------------------- 1 | Notifications To Pager -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/dependencies-accessors/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /TelePager-bot/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'TelePager' 2 | 3 | -------------------------------------------------------------------------------- /TelePager-bot/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /previ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/previ.jpg -------------------------------------------------------------------------------- /Schematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/Schematic.jpg -------------------------------------------------------------------------------- /NotificationsToPager-android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /TelePager-bot/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 28 23:03:00 YEKT 2022 2 | gradle.version=7.1.1 3 | -------------------------------------------------------------------------------- /Variant by Dmitry Stupak/Schematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/Variant by Dmitry Stupak/Schematic.jpg -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Notifications To Pager 3 | -------------------------------------------------------------------------------- /TelePager-bot/.gradle/checksums/checksums.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/checksums/checksums.lock -------------------------------------------------------------------------------- /TelePager-bot/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /TelePager-bot/.gradle/checksums/md5-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/checksums/md5-checksums.bin -------------------------------------------------------------------------------- /TelePager-bot/.gradle/checksums/sha1-checksums.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/checksums/sha1-checksums.bin -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/7.1.1/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/7.1.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /TelePager-bot/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /Variant by Dmitry Stupak/UniversalPagerTransmiter.lay6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/Variant by Dmitry Stupak/UniversalPagerTransmiter.lay6 -------------------------------------------------------------------------------- /NotificationsToPager-android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/executionHistory/executionHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/7.1.1/executionHistory/executionHistory.bin -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/executionHistory/executionHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/7.1.1/executionHistory/executionHistory.lock -------------------------------------------------------------------------------- /TelePager-bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /TelePager-bot/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /TelePager-bot/.gradle/7.1.1/dependencies-accessors/dependencies-accessors.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/TelePager-bot/.gradle/7.1.1/dependencies-accessors/dependencies-accessors.lock -------------------------------------------------------------------------------- /NotificationsToPager-android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotPixelChannel/UniversalPagerTransmiter/HEAD/NotificationsToPager-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /TelePager-bot/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/MemoryFree.h: -------------------------------------------------------------------------------- 1 | // memoryFree header 2 | // From http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15 3 | // ...written by user "mem". 4 | 5 | #ifndef MEMORY_FREE_H 6 | #define MEMORY_FREE_H 7 | 8 | int freeMemory(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /NotificationsToPager-android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 01 17:25:09 YEKT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /NotificationsToPager-android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /NotificationsToPager-android/settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "Notifications To Pager" 10 | include ':app' 11 | -------------------------------------------------------------------------------- /TelePager-bot/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /TelePager-bot/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'org.example' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 14 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 15 | 16 | implementation 'org.telegram:telegrambots:5.7.1' 17 | implementation 'com.fazecast:jSerialComm:2.9.1' 18 | } 19 | 20 | test { 21 | useJUnitPlatform() 22 | } -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/test/java/ru/example/notificationstopager/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package ru.example.notificationstopager; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/MemoryFree.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | // should use uinstd.h to define sbrk but Due causes a conflict 3 | extern "C" char* sbrk(int incr); 4 | #else // __ARM__ 5 | extern char *__brkval; 6 | #endif // __arm__ 7 | 8 | int freeMemory() { 9 | char top; 10 | #ifdef __arm__ 11 | return &top - reinterpret_cast(sbrk(0)); 12 | #elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151) 13 | return &top - __brkval; 14 | #else // __arm__ 15 | return __brkval ? &top - __brkval : &top - __malloc_heap_start; 16 | #endif // __arm__ 17 | } 18 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/CmdProc.h: -------------------------------------------------------------------------------- 1 | #ifndef _CMDPROC_H_ 2 | #define _CMDPROC_H_ 3 | #include "Storage.h" 4 | 5 | 6 | 7 | void printWelcome(Rf7021 rf); 8 | void processCommand(); 9 | void printWelcome(); 10 | void printFreeMode(); 11 | void printFreeModeAdv(); 12 | 13 | void printSending(); 14 | void printSent(); 15 | void printTrError(); 16 | void pstr(String &str); 17 | void reserBuf(); 18 | 19 | void printListMode(); 20 | void printPager(Pager *pager, bool detailed, bool wHeader); // Print pager info 21 | void printAddDevice(byte stepC, Pager *pager) ; 22 | void printSpecMode(); 23 | void printFixAdded(byte *fixed); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /TelePager-bot/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /NotificationsToPager-android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = '1.6.20-RC2' 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath "com.android.tools.build:gradle:7.0.4" 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | task clean(type: Delete) { 18 | delete rootProject.buildDir 19 | } -------------------------------------------------------------------------------- /NotificationsToPager-android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UniversalPagerTransmiter 2 | ![Preview](previ.jpg) 3 | 4 | Universal POKSAG message transmitter for one pager or group of pagers. All work with the device is carried out according to the UART protocol and any terminal. Assembled on Arduino Nano and RF7021SE transmitter module 5 | 6 | Youtube: https://youtu.be/_yl8x5P07AI 7 | 8 | Based on: https://github.com/SinuXVR/arduino-pocsag-transcoder 9 | 10 | The ADF7021 (or RF7021SE module) must operate with a 14.7456 MHz TCXO and with at least 2.5 ppm of frequency stability or better. You could use also 12.2880 MHz TCXO. Any other TCXO frequency is not supported. 11 | For working on 145-160MHz needs be added by an external 18 nH inductor between L1 and L2 pins of ADF7021. 12 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Storage.cpp: -------------------------------------------------------------------------------- 1 | #include "Storage.h" 2 | #include 3 | 4 | void clearEE() { 5 | for (int i = 0; i < EEPROM.length(); i++) EEPROM.update(i, 255); 6 | } 7 | // Add pager to EEPROM (addr 0-20) 8 | bool addPager(Pager *pager, bool toDel) { 9 | Pager lPager = *pager; 10 | if (lPager.addr > STORAGE_COUNT) 11 | return false; 12 | 13 | if (!toDel) { 14 | lPager.crc[0] = CRC[0]; 15 | lPager.crc[1] = CRC[1]; 16 | }else{ 17 | lPager.crc[0] = 0; 18 | lPager.crc[1] = 0; 19 | } 20 | EEPROM.put(lPager.addr * sizeof(Pager), lPager); 21 | return true; 22 | } 23 | 24 | void getPager(byte address, struct Pager *pager) { 25 | EEPROM.get(address*sizeof(Pager), *pager); 26 | } 27 | -------------------------------------------------------------------------------- /NotificationsToPager-android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Storage.h: -------------------------------------------------------------------------------- 1 | #ifndef _STORAGE_H 2 | #define _STORAGE_H 3 | 4 | #include 5 | #include "Arduino.h" 6 | 7 | #define PAGER_SIZE 161 8 | #define STORAGE_COUNT 20 9 | 10 | const byte CRC[2] = {0x45, 0x99}; 11 | 12 | typedef struct Pager { 13 | byte addr: 8; // Address in EEPROM 14 | char alias[10]; // Readable name 15 | uint32_t cap : 24; // CAP code 16 | uint32_t frequency : 20; // Frequency 17 | byte msgSource: 2; // Message source (0-3) 18 | byte enconding: 2; // Text enconding 19 | byte inversion : 1; // Bit inversion 20 | word rate: 12; // Bautrate (512, 1200, 2400) 21 | byte crc[2]; // Index bytes for detecting 22 | }; 23 | 24 | 25 | void clearEE(); 26 | bool addPager(Pager *pager, bool toDel); 27 | void getPager(byte address, struct Pager *pager) ; 28 | Pager* getPagers(); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /TelePager-bot/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/androidTest/java/ru/example/notificationstopager/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package ru.example.notificationstopager; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("ru.example.notificationstopager", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /NotificationsToPager-android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /NotificationsToPager-android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | apply plugin: 'kotlin-android' 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "ru.example.notificationstopager" 11 | minSdk 21 12 | targetSdk 31 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | 30 | buildFeatures { 31 | viewBinding = true 32 | } 33 | } 34 | 35 | dependencies { 36 | 37 | implementation 'androidx.appcompat:appcompat:1.4.1' 38 | implementation 'com.google.android.material:material:1.5.0' 39 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 40 | testImplementation 'junit:junit:4.+' 41 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 43 | implementation "androidx.core:core-ktx:+" 44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 45 | 46 | implementation 'me.aflak.libraries:bluetooth:1.3.9' 47 | } 48 | -------------------------------------------------------------------------------- /TelePager-bot/src/main/java/Main.java: -------------------------------------------------------------------------------- 1 | import org.telegram.telegrambots.meta.TelegramBotsApi; 2 | import org.telegram.telegrambots.meta.api.objects.Update; 3 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 4 | import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; 5 | 6 | public class Main { 7 | static String chatId; 8 | 9 | 10 | public static void main(String[] args) { 11 | PagerBot bot = new PagerBot(); 12 | Serial serial = new Serial("COM4", 9600); 13 | 14 | bot.setEventListener(new PagerBot.BotReceive() { 15 | @Override 16 | public void onNewEvent(Update update) { 17 | chatId = update.getMessage().getChatId().toString(); 18 | if (update.hasMessage() && update.getMessage().hasText()) { 19 | serial.sendMessage(update.getMessage().getText()); 20 | } 21 | } 22 | }); 23 | 24 | try { 25 | TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); 26 | botsApi.registerBot(bot); 27 | } catch (TelegramApiException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | 32 | serial.setPortListener(new Serial.SerialPortEvent() { 33 | @Override 34 | public void onReceive(String msg) { 35 | if (chatId != null && msg.contains("Message")) 36 | bot.sendMessage(chatId, "Message sent..."); 37 | } 38 | }); 39 | 40 | if (serial.connect()) { 41 | System.out.println("Port opened"); 42 | } 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/java/ru/example/notificationstopager/NotificationService.kt: -------------------------------------------------------------------------------- 1 | package ru.example.notificationstopager 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.service.notification.NotificationListenerService 6 | import android.service.notification.StatusBarNotification 7 | import androidx.localbroadcastmanager.content.LocalBroadcastManager 8 | 9 | 10 | class NotificationService : NotificationListenerService() { 11 | var context: Context? = null 12 | var title: String = "" 13 | var text: String = "" 14 | 15 | override fun onCreate() { 16 | super.onCreate() 17 | context = applicationContext 18 | } 19 | 20 | override fun onListenerConnected() { 21 | super.onListenerConnected() 22 | for (noti in getActiveNotifications()){ 23 | onNotificationPosted(noti) 24 | } 25 | } 26 | 27 | override fun onNotificationPosted(sbn: StatusBarNotification) { 28 | title = "" 29 | text = "" 30 | val pack = sbn.packageName 31 | var ticker = "" 32 | if (sbn.notification.tickerText != null) { 33 | ticker = sbn.notification.tickerText.toString() 34 | } 35 | 36 | title = sbn.notification.extras?.getString("android.title") ?: "" 37 | text = sbn.notification.extras?.getString("android.text") ?: "" 38 | 39 | if(title.isEmpty() && text.isEmpty()) 40 | return 41 | 42 | val msgrcv = Intent("Msg") 43 | with(msgrcv) { 44 | putExtra("package", pack) 45 | putExtra("ticker", ticker) 46 | putExtra("title", title) 47 | putExtra("text", text) 48 | } 49 | 50 | 51 | LocalBroadcastManager.getInstance(context!!).sendBroadcast(msgrcv) 52 | } 53 | 54 | override fun onNotificationRemoved(sbn: StatusBarNotification) { 55 | 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /TelePager-bot/src/main/java/PagerBot.java: -------------------------------------------------------------------------------- 1 | import org.telegram.telegrambots.bots.TelegramLongPollingBot; 2 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 3 | import org.telegram.telegrambots.meta.api.objects.Update; 4 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 5 | 6 | public class PagerBot extends TelegramLongPollingBot { 7 | 8 | public interface BotReceive { 9 | void onNewEvent(Update update); 10 | } 11 | 12 | BotReceive eventListener; 13 | 14 | public void setEventListener(BotReceive eventListener) { 15 | this.eventListener = eventListener; 16 | } 17 | 18 | @Override 19 | public void onUpdateReceived(Update update) { 20 | if (eventListener != null) 21 | eventListener.onNewEvent(update); 22 | // We check if the update has a message and the message has text 23 | if (update.hasMessage() && update.getMessage().hasText()) { 24 | SendMessage message = new SendMessage(); // Create a SendMessage object with mandatory fields 25 | message.setChatId(update.getMessage().getChatId().toString()); 26 | message.setText(update.getMessage().getText()); 27 | 28 | System.out.println(update.getMessage().getText()); 29 | } 30 | 31 | 32 | } 33 | 34 | public void sendMessage(String chatId, String msg){ 35 | try { 36 | SendMessage message = new SendMessage(); // Create a SendMessage object with mandatory fields 37 | message.setChatId(chatId); 38 | message.setText(msg); 39 | execute(message); 40 | } catch (TelegramApiException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | @Override 46 | public String getBotUsername() { 47 | return "Your Bot Name"; 48 | } 49 | 50 | @Override 51 | public String getBotToken() { 52 | return "Paste your token"; 53 | } 54 | } -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Cmd.h: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Copyright (C) 2009 FreakLabs 3 | All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the the copyright holder nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | SUCH DAMAGE. 26 | Originally written by Christopher Wang aka Akiba. 27 | Please post support questions to the FreakLabs forum. 28 | *******************************************************************/ 29 | /*! 30 | \file 31 | \ingroup 32 | */ 33 | /**************************************************************************/ 34 | #ifndef CMD_H 35 | #define CMD_H 36 | 37 | #define MAX_MSG_SIZE 120 38 | #include 39 | 40 | // command line structure 41 | typedef struct _cmd_t 42 | { 43 | char *cmd; 44 | void (*func)(int argc, char **argv); 45 | struct _cmd_t *next; 46 | } cmd_t; 47 | 48 | void cmdInit(uint32_t speed); 49 | void cmdPoll(); 50 | void clearCmd(); 51 | void pauseCmd(byte pause); 52 | void cmdAdd(char *name, void (*func)(int argc, char **argv)); 53 | uint32_t cmdStr2Num(char *str, uint8_t base); 54 | 55 | #endif //CMD_H 56 | -------------------------------------------------------------------------------- /TelePager-bot/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /NotificationsToPager-android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 32 | 33 | 41 | 42 | 50 | 51 | 59 | 60 | 68 | 69 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/java/ru/example/notificationstopager/Translit.java: -------------------------------------------------------------------------------- 1 | package ru.example.notificationstopager; 2 | 3 | public class Translit { 4 | public static String cyr2lat(char ch){ 5 | switch (ch){ 6 | case 'А': return "A"; 7 | case 'Б': return "B"; 8 | case 'В': return "V"; 9 | case 'Г': return "G"; 10 | case 'Д': return "D"; 11 | case 'Е': return "E"; 12 | case 'Ё': return "JE"; 13 | case 'Ж': return "ZH"; 14 | case 'З': return "Z"; 15 | case 'И': return "I"; 16 | case 'Й': return "Y"; 17 | case 'К': return "K"; 18 | case 'Л': return "L"; 19 | case 'М': return "M"; 20 | case 'Н': return "N"; 21 | case 'О': return "O"; 22 | case 'П': return "P"; 23 | case 'Р': return "R"; 24 | case 'С': return "S"; 25 | case 'Т': return "T"; 26 | case 'У': return "U"; 27 | case 'Ф': return "F"; 28 | case 'Х': return "KH"; 29 | case 'Ц': return "C"; 30 | case 'Ч': return "CH"; 31 | case 'Ш': return "SH"; 32 | case 'Щ': return "JSH"; 33 | case 'Ъ': return "HH"; 34 | case 'Ы': return "IH"; 35 | case 'Ь': return "JH"; 36 | case 'Э': return "EH"; 37 | case 'Ю': return "JU"; 38 | case 'Я': return "JA"; 39 | 40 | case 'а': return "a"; 41 | case 'б': return "b"; 42 | case 'в': return "v"; 43 | case 'г': return "g"; 44 | case 'д': return "d"; 45 | case 'е': return "e"; 46 | case 'ё': return "je"; 47 | case 'ж': return "zh"; 48 | case 'з': return "z"; 49 | case 'и': return "i"; 50 | case 'й': return "y"; 51 | case 'к': return "k"; 52 | case 'л': return "l"; 53 | case 'м': return "m"; 54 | case 'н': return "n"; 55 | case 'о': return "o"; 56 | case 'п': return "p"; 57 | case 'р': return "r"; 58 | case 'с': return "s"; 59 | case 'т': return "t"; 60 | case 'у': return "u"; 61 | case 'ф': return "f"; 62 | case 'х': return "kh"; 63 | case 'ц': return "c"; 64 | case 'ч': return "ch"; 65 | case 'ш': return "sh"; 66 | case 'щ': return "jsh"; 67 | case 'ъ': return "hh"; 68 | case 'ы': return "ih"; 69 | case 'ь': return "jh"; 70 | case 'э': return "eh"; 71 | case 'ю': return "ju"; 72 | case 'я': return "ja"; 73 | default: return String.valueOf(ch); 74 | } 75 | } 76 | 77 | public static String cyr2lat(String s){ 78 | StringBuilder sb = new StringBuilder(s.length()*2); 79 | for(char ch: s.toCharArray()){ 80 | sb.append(cyr2lat(ch)); 81 | } 82 | return sb.toString(); 83 | } 84 | } -------------------------------------------------------------------------------- /TelePager-bot/src/main/java/Serial.java: -------------------------------------------------------------------------------- 1 | import com.fazecast.jSerialComm.SerialPort; 2 | import com.fazecast.jSerialComm.SerialPortDataListener; 3 | 4 | import java.io.InputStream; 5 | import java.nio.charset.StandardCharsets; 6 | 7 | import static com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_BLOCKING; 8 | 9 | public class Serial { 10 | public interface SerialPortEvent { 11 | void onReceive(String msg); 12 | } 13 | 14 | private String port; 15 | private int rate; 16 | private SerialPort serialPort; 17 | private SerialPortEvent portListener; 18 | 19 | public Serial(String port, int rate) { 20 | this.port = port; 21 | this.rate = rate; 22 | } 23 | 24 | public void setPortListener(SerialPortEvent portListener) { 25 | this.portListener = portListener; 26 | } 27 | 28 | public boolean connect() { 29 | if (port.toLowerCase().split("com").length < 2) { 30 | System.out.println(port + " is really correct?"); 31 | return false; 32 | } 33 | SerialPort[] avPorts = SerialPort.getCommPorts(); 34 | for (SerialPort avPort : avPorts) { 35 | if (avPort.getSystemPortPath().toLowerCase().contains(port.toLowerCase())) { 36 | serialPort = avPort; 37 | serialPort.setComPortTimeouts(TIMEOUT_READ_BLOCKING, 15000, 5000); 38 | break; 39 | } 40 | } 41 | if (serialPort == null) { 42 | System.out.println("Port NULL"); 43 | return false; 44 | } 45 | if (serialPort.isOpen()) { 46 | System.out.println("Port is busy"); 47 | return false; 48 | } 49 | serialPort.setBaudRate(rate); 50 | serialPort.openPort(); 51 | serialPort.addDataListener(new SerialPortDataListener() { 52 | @Override 53 | public int getListeningEvents() { 54 | return 3; 55 | } 56 | 57 | @Override 58 | public void serialEvent(com.fazecast.jSerialComm.SerialPortEvent event) { 59 | switch (event.getEventType()) { 60 | case SerialPort.LISTENING_EVENT_DATA_AVAILABLE: { 61 | StringBuilder msg = new StringBuilder(); 62 | InputStream in = serialPort.getInputStream(); 63 | try { 64 | for (int j = 0; j < in.available(); ++j) 65 | msg.append((char) in.read()); 66 | System.out.print(msg); 67 | if (portListener != null) 68 | portListener.onReceive(msg.toString()); 69 | in.close(); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | break; 74 | } 75 | 76 | case SerialPort.LISTENING_EVENT_DATA_WRITTEN: { 77 | break; 78 | } 79 | } 80 | } 81 | }); 82 | 83 | 84 | return serialPort.isOpen(); 85 | } 86 | 87 | public boolean sendMessage(String msg) { 88 | if (!serialPort.isOpen()) { 89 | System.out.println("Port is closed"); 90 | return false; 91 | } 92 | if (msg == null || msg.isEmpty()) { 93 | System.out.println("Empty message"); 94 | return false; 95 | } 96 | msg = msg + "\r"; 97 | byte[] buf = msg.getBytes(StandardCharsets.UTF_8); 98 | serialPort.writeBytes(msg.getBytes(StandardCharsets.UTF_8), buf.length); 99 | return true; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Rf7021.h: -------------------------------------------------------------------------------- 1 | /* 2 | Simple library for using the RF7021SE (ADF7021) module to transfer binary data 3 | 4 | RF7021SE module pinout: 5 | 6 | | VCC | PAC (not used) | 7 | | CE | SLE | 8 | | SDATA | SREAD | 9 | | NC (not used) | SCLK | 10 | | TxRxData | SWD (not used) | 11 | | MUX (not used) | TxRxCLK | 12 | | GND | GND | 13 | 14 | Copyright (c) 2021, SinuX. All rights reserved. 15 | 16 | This library is distributed "as is" without any warranty. 17 | See MIT License for more details. 18 | */ 19 | 20 | #ifndef _RF7021_H_ 21 | #define _RF7021_H_ 22 | 23 | #include "Arduino.h" 24 | 25 | class Rf7021 { 26 | 27 | public: 28 | Rf7021(); 29 | 30 | // Wiring 31 | void setTxRxDataPin(byte txRxDataPin); 32 | void setTxRxCLKPin(byte txRxCLKPin); 33 | void setCEPin(byte cePin); 34 | void setSREADPin(byte sreadPin); 35 | void setSLEPin(byte slePin); 36 | void setSDATAPin(byte sdataPin); 37 | void setSCLKPin(byte sclkPin); 38 | 39 | // Readback 40 | word getSiliconRev(); 41 | float getTemp(); 42 | float getVoltage(); 43 | 44 | // Setup 45 | void setXtalFrequency(uint32_t xtalFrequency); 46 | uint32_t getXtalFrequency(); 47 | void setXtalBias(byte xtalBias); 48 | void setCpCurrent(byte cpCurrent); 49 | void setHasExternalInductor(byte hasExternalInductor); 50 | void setmModulationScheme(byte modulationScheme); 51 | void setPowerAmplifierEnabled(byte powerAmplifierEnabled); 52 | void setPowerAmplifierRamping(byte powerAmplifierRamping); 53 | void setPowerAmplifierBias(byte powerAmplifierBias); 54 | void setPowerAmplifierPower(byte powerAmplifierPower); 55 | void setDataInvertType(byte dataInvertType); 56 | void setRCosineAlpha(byte rCosineAlpha); 57 | void setDataRate(word dataRate); 58 | void setFrequency(uint32_t frequency); 59 | uint32_t getFrequency(); 60 | void setFrequencyDeviation(word frequencyDev); 61 | void setDataInvertEnabled(byte dataInvertEnabled); 62 | 63 | // Transmission 64 | void txTest(byte testMode, word duration); 65 | bool sendMessage(byte *message, word messageLength); 66 | 67 | private: 68 | typedef union { 69 | uint32_t data; 70 | word dataLower; 71 | word dataUpper; 72 | byte dataBytes[4]; 73 | } RfReg; 74 | 75 | typedef struct { 76 | // Wiring 77 | byte txRxDataPin, txRxCLKPin; 78 | byte cePin, sreadPin, slePin, sdataPin, sclkPin; 79 | 80 | // Core settings 81 | uint32_t xtalFrequency; 82 | byte xtalBias; 83 | byte cpCurrent; 84 | byte hasExternalInductor; 85 | 86 | // Transmission settings 87 | byte modulationScheme; 88 | byte powerAmplifierEnabled; 89 | byte powerAmplifierRamping; 90 | byte powerAmplifierBias; 91 | byte powerAmplifierPower; 92 | byte dataInvertType; 93 | byte rCosineAlpha; 94 | word dataRate; 95 | uint32_t frequency; 96 | word frequencyDeviation; 97 | byte dataInvertEnabled; 98 | 99 | union { 100 | RfReg r0; 101 | struct { 102 | byte addressBits : 4; 103 | word fractionalN : 15; 104 | word integerN : 8; 105 | byte rxMode : 1; 106 | byte uartMode : 1; 107 | byte muxOut : 3; 108 | } R0; 109 | }; 110 | union { 111 | RfReg r1; 112 | struct { 113 | byte addressBits : 4; 114 | byte rCounter : 3; 115 | byte clockoutDivide : 4; 116 | byte xtalDoubler : 1; 117 | byte xoscEnable : 1; 118 | byte xtalBias : 2; 119 | byte cpCurrent : 2; 120 | byte vcoEnable : 1; 121 | byte rfDivideBy2 : 1; 122 | byte vcoBias : 4; 123 | byte vcoAdjust : 2; 124 | byte vcoInductor : 1; 125 | } R1; 126 | }; 127 | union { 128 | RfReg r2; 129 | struct { 130 | byte addressBits : 4; 131 | byte modulationScheme : 3; 132 | byte paEnable : 1; 133 | byte paRamp : 3; 134 | byte paBias : 2; 135 | byte powerAmplifier : 6; 136 | word txFrequencyDeviation : 9; 137 | byte txDataInvert : 2; 138 | byte rcosineAlpha : 1; 139 | } R2; 140 | }; 141 | union { 142 | RfReg r3; 143 | struct { 144 | byte addressBits : 4; 145 | byte bbosClkDivide: 2; 146 | byte demClkDivide : 4; 147 | byte cdrClkDivide : 8; 148 | byte seqClkDivide : 8; 149 | byte agcClkDivide : 6; 150 | } R3; 151 | }; 152 | union { 153 | RfReg r15; 154 | struct { 155 | byte addressBits : 4; 156 | byte rxTestMode : 4; 157 | byte txTestMode : 3; 158 | byte sdTestMode : 3; 159 | byte cpTestMode : 3; 160 | byte clkMux : 3; 161 | byte pllTestMode : 4; 162 | byte analogTestMode : 4; 163 | byte forceLdHigh : 1; 164 | byte reg1Pd : 1; 165 | byte calOverride : 2; 166 | } R15; 167 | }; 168 | } RfConfig; 169 | 170 | RfConfig rfConfig; 171 | 172 | void writeReg(RfReg *reg); 173 | RfReg readReg(word readbackConfig); 174 | 175 | void powerOn(); 176 | void powerOff(); 177 | }; 178 | 179 | 180 | #endif /* _RF7021_H_ */ 181 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /NotificationsToPager-android/app/src/main/java/ru/example/notificationstopager/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package ru.example.notificationstopager 2 | 3 | import android.Manifest 4 | import android.Manifest.permission.BLUETOOTH_CONNECT 5 | import android.app.NotificationChannel 6 | import android.app.NotificationManager 7 | import android.bluetooth.BluetoothDevice 8 | import android.content.* 9 | import android.content.pm.PackageManager 10 | import android.os.Build 11 | import android.os.Bundle 12 | import android.os.Handler 13 | import android.os.Looper 14 | import android.util.Log 15 | import androidx.appcompat.app.AppCompatActivity 16 | import androidx.core.app.ActivityCompat 17 | import androidx.core.app.NotificationCompat 18 | import androidx.core.app.NotificationManagerCompat 19 | import androidx.localbroadcastmanager.content.LocalBroadcastManager 20 | import me.aflak.bluetooth.Bluetooth 21 | import me.aflak.bluetooth.interfaces.DeviceCallback 22 | import ru.example.notificationstopager.databinding.ActivityMainBinding 23 | import java.net.URLEncoder 24 | import java.nio.charset.Charset 25 | import java.nio.charset.StandardCharsets 26 | import java.util.* 27 | 28 | class MainActivity : AppCompatActivity() { 29 | lateinit var binding: ActivityMainBinding; 30 | 31 | var capV: Long = 0 32 | var freqV: Long = 0 33 | var invV: Boolean = false 34 | lateinit var prefs: SharedPreferences 35 | 36 | //val MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") //для HC-05, на HC-06/04 возможно другой UUID 37 | val bluetooth = Bluetooth(this); 38 | 39 | 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | binding = ActivityMainBinding.inflate(layoutInflater) 43 | setContentView(binding.root) 44 | prefs = getSharedPreferences("prefs", MODE_PRIVATE) 45 | 46 | capV = prefs.getLong("cap", 0) 47 | freqV = prefs.getLong("freq", 0) 48 | invV = prefs.getBoolean("inv", false) 49 | with(binding) { 50 | 51 | cap.setText(if (capV > 0) capV.toString() else "") 52 | freq.setText(if (freqV > 0) freqV.toString() else "") 53 | inv.isChecked = invV 54 | } 55 | 56 | binding.save.setOnClickListener { 57 | with(prefs.edit()) { 58 | putLong("cap", binding.cap.text.toString().toLong()).apply() 59 | putLong("freq", binding.freq.text.toString().toLong()).apply() 60 | putBoolean("inv", binding.inv.isChecked).apply() 61 | } 62 | } 63 | 64 | binding.chPerm.setOnClickListener { 65 | reqPermissions() 66 | } 67 | 68 | binding.testNotif.setOnClickListener { 69 | createNotif() 70 | } 71 | 72 | LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, IntentFilter("Msg")); 73 | 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 75 | if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED) { 76 | 77 | } 78 | } 79 | btInit() 80 | // startService(Intent(this,NotificationService::class.java)) 81 | } 82 | 83 | fun btInit() { 84 | bluetooth.onStart(); 85 | bluetooth.setDeviceCallback(object : DeviceCallback { 86 | override fun onDeviceConnected(device: BluetoothDevice?) { 87 | 88 | } 89 | 90 | override fun onDeviceDisconnected(device: BluetoothDevice?, message: String?) { 91 | 92 | } 93 | 94 | override fun onMessage(message: ByteArray?) { 95 | 96 | } 97 | 98 | override fun onError(errorCode: Int) { 99 | 100 | } 101 | 102 | override fun onConnectError(device: BluetoothDevice?, message: String?) { 103 | 104 | } 105 | }) 106 | 107 | bluetooth.connectToName("BT Super"); 108 | 109 | } 110 | 111 | fun sendToBt(msg: String) { 112 | 113 | if (bluetooth.isConnected) 114 | bluetooth.send(msg, StandardCharsets.UTF_8) 115 | } 116 | 117 | fun createNotif() { 118 | var builder = NotificationCompat.Builder(this, "sdfsdf") 119 | .setSmallIcon(R.drawable.ic_launcher_foreground) 120 | .setContentTitle("Важное событие") 121 | .setContentText("Тестируем пейджер...") 122 | .setPriority(NotificationCompat.PRIORITY_HIGH) 123 | 124 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 125 | val name = "Notifi" 126 | val descriptionText = "Some text" 127 | val importance = NotificationManager.IMPORTANCE_DEFAULT 128 | val channel = NotificationChannel("sdfsdf", name, importance).apply { 129 | description = descriptionText 130 | } 131 | // Register the channel with the system 132 | val notificationManager: NotificationManager = 133 | getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 134 | notificationManager.createNotificationChannel(channel) 135 | } 136 | 137 | with(NotificationManagerCompat.from(this)) { 138 | // notificationId is a unique int for each notification that you must define 139 | notify(123, builder.build()) 140 | } 141 | } 142 | 143 | 144 | private fun reqPermissions(): Boolean { 145 | startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) 146 | 147 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 148 | ActivityCompat.requestPermissions( 149 | this, listOf( 150 | Manifest.permission.BLUETOOTH_SCAN, 151 | BLUETOOTH_CONNECT 152 | ).toTypedArray(), 100 153 | ) 154 | } 155 | 156 | 157 | return false 158 | } 159 | 160 | private val onNotice: BroadcastReceiver = object : BroadcastReceiver() { 161 | override fun onReceive(context: Context, intent: Intent) { 162 | 163 | // val title = Translit.cyr2lat(intent.getStringExtra("title")) 164 | // val text = Translit.cyr2lat(intent.getStringExtra("text")) 165 | val title = intent.getStringExtra("title") 166 | val text = intent.getStringExtra("text") 167 | Log.e("messg", title.plus(", ").plus(text)) 168 | sendToBt("send f $freqV c $capV e 1 i ${if (invV) 1 else 0} m $title: $text\r") 169 | 170 | 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /TelePager-bot/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /NotificationsToPager-android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/PocsagEncoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple yet fully functional POCSAG encoder implementation capable 3 | * of generating any kind of message (tone, numeric or alphanumeric) 4 | * 5 | * Alphanumeric messages can be encoded using one of 4 encodings 6 | * 7 | * Copyright (c) 2021, SinuX. All rights reserved. 8 | * 9 | * This library is distributed "as is" without any warranty. 10 | * See MIT License for more details. 11 | */ 12 | 13 | #ifndef _POCSAGENCODER_H_ 14 | #define _POCSAGENCODER_H_ 15 | 16 | #define PCSG_PREAMBLE_LENGTH 80 // Preamble length in bytes (must be >= 72) 17 | #define PCSG_BATCHES_COUNT 5 // Batches count (5 batches can handle 185 to 225 alphanumeric symbols) 18 | #define PCSG_MESSAGE_LENGTH PCSG_PREAMBLE_LENGTH + 68 * PCSG_BATCHES_COUNT // Max message length in bytes including preamble and all batches 19 | #define PCSG_EOT_CHAR 0x04 // Alphanumeric message text termination character code 20 | 21 | #include "Arduino.h" 22 | 23 | class PocsagEncoder { 24 | 25 | public: 26 | PocsagEncoder(); 27 | 28 | void setCapCode(uint32_t capCode); 29 | void setSource(byte source); 30 | void setEncodingId(byte encodingId); 31 | void setPrintMessage(byte printMessage); 32 | 33 | typedef union { 34 | struct { 35 | byte parityBits : 1; 36 | word crcBits : 10; 37 | uint32_t payloadBits : 20; // 18 address + 2 source bits or 20 message bits 38 | byte messageType : 1; 39 | }; 40 | uint32_t data; 41 | } PocsagCW; 42 | 43 | typedef struct { 44 | PocsagCW codeWords[2]; 45 | } PocsagFrame; 46 | 47 | typedef struct { 48 | uint32_t fsc; 49 | PocsagFrame frames[8]; 50 | } PocsagBatch; 51 | 52 | typedef struct { 53 | word messageLength; 54 | union { 55 | struct { 56 | byte preamble[PCSG_PREAMBLE_LENGTH]; 57 | PocsagBatch batches[PCSG_BATCHES_COUNT]; 58 | }; 59 | byte dataBytes[PCSG_MESSAGE_LENGTH]; 60 | }; 61 | } PocsagMessage; 62 | 63 | PocsagMessage encodeNumeric(String messageText); 64 | PocsagMessage encodeAlphanumeric(String messageText); 65 | PocsagMessage encodeTone(); 66 | 67 | private: 68 | uint32_t capCode; 69 | byte source; 70 | byte encodingId; 71 | byte printMessage; 72 | 73 | byte getPocsagNumericCharacter(byte asciiChar); 74 | byte getPocsagAlphanumericCharacter(word utf8Char, byte encodingId); 75 | 76 | struct { 77 | word offset; 78 | byte length; 79 | } PCSG_ENCODINGS[4] = { 80 | {48, 63}, // 0 - Latin (Motorola Advisor) 81 | {111, 75}, // 1 - Latin/Cyrillic (Motorola Advisor Linguist, Scriptor LX2 Linguist, NEC Maxima, Optima, Truly Supervisor) 82 | {186, 65}, // 2 - Latin/Cyrillic (Philips PRG2220, PRG2310) 83 | {251, 67} // 3 - Cyrillic (Motorola Advisor, Scriptor LX1) 84 | }; 85 | }; 86 | 87 | static const byte PCSG_ALPHABET[318][2] PROGMEM = { 88 | // Numeric 89 | {'0', 0x0}, {'1', 0x1}, {'2', 0x2}, {'3', 0x3}, {'4', 0x4}, {'5', 0x5}, {'6', 0x6}, {'7', 0x7}, {'8', 0x8}, {'9', 0x9}, {'*', 0xA}, {'U', 0xB}, {' ', 0xC}, {'-', 0xD}, {')', 0xE}, {'(', 0xF}, 90 | 91 | // Alphanumeric common 92 | {' ', 0x20}, {'!', 0x21}, {'"', 0x22}, {'#', 0x23}, {'$', 0x24}, {'%', 0x25}, {'&', 0x26}, {'\'', 0x27}, {'(', 0x28}, {')', 0x29}, {'*', 0x2A}, {'+', 0x2B}, {',', 0x2C}, {'-', 0x2D}, {'.', 0x2E}, {'/', 0x2F}, 93 | {'0', 0x30}, {'1', 0x31}, {'2', 0x32}, {'3', 0x33}, {'4', 0x34}, {'5', 0x35}, {'6', 0x36}, {'7', 0x37}, {'8', 0x38}, {'9', 0x39}, {':', 0x3A}, {';', 0x3B}, {'<', 0x3C}, {'=', 0x3D}, {'>', 0x3E}, {'?', 0x3F}, 94 | 95 | // 0 - Alphanumeric Latin (Motorola Advisor) 96 | {'@', 0x40}, {'A', 0x41}, {'B', 0x42}, {'C', 0x43}, {'D', 0x44}, {'E', 0x45}, {'F', 0x46}, {'G', 0x47}, {'H', 0x48}, {'I', 0x49}, {'J', 0x4A}, {'K', 0x4B}, {'L', 0x4C}, {'M', 0x4D}, {'N', 0x4E}, {'O', 0x4F}, 97 | {'P', 0x50}, {'Q', 0x51}, {'R', 0x52}, {'S', 0x53}, {'T', 0x54}, {'U', 0x55}, {'V', 0x56}, {'W', 0x57}, {'X', 0x58}, {'Y', 0x59}, {'Z', 0x5A}, {'[', 0x5B}, {'\\', 0x5C}, {']', 0x5D}, {'^', 0x5E}, {'_', 0x5F}, 98 | {'`', 0x60}, {'a', 0x61}, {'b', 0x62}, {'c', 0x63}, {'d', 0x64}, {'e', 0x65}, {'f', 0x66}, {'g', 0x67}, {'h', 0x68}, {'i', 0x69}, {'j', 0x6A}, {'k', 0x6B}, {'l', 0x6C}, {'m', 0x6D}, {'n', 0x6E}, {'o', 0x6F}, 99 | {'p', 0x70}, {'q', 0x71}, {'r', 0x72}, {'s', 0x73}, {'t', 0x74}, {'u', 0x75}, {'v', 0x76}, {'w', 0x77}, {'x', 0x78}, {'y', 0x79}, {'z', 0x7A}, {'{', 0x7B}, {'|', 0x7C}, {'}', 0x7D}, {'~', 0x7E}, 100 | 101 | // 1 - Alphanumeric Latin/Cyrillic (Motorola Advisor Linguist, Scriptor LX2 Linguist, NEC Maxima, Optima, Truly Supervisor) 102 | {'@', 0x40}, {'A', 0x41}, {192, 0x41}, {'B', 0x42}, {194, 0x42}, {'C', 0x43}, {209, 0x43}, {'D', 0x44}, {'E', 0x45}, {197, 0x45}, {'F', 0x46}, {'G', 0x47}, {'H', 0x48}, {205, 0x48}, {'I', 0x49}, {'J', 0x4A}, {'K', 0x4B}, {202, 0x4B}, {'L', 0x4C}, {'M', 0x4D}, {204, 0x4D}, {'N', 0x4E}, {'O', 0x4F}, {206, 0x4F}, 103 | {'P', 0x50}, {208, 0x50}, {'Q', 0x51}, {'R', 0x52}, {'S', 0x53}, {'T', 0x54}, {210, 0x54}, {'U', 0x55}, {'V', 0x56}, {'W', 0x57}, {'X', 0x58}, {213, 0x58}, {'Y', 0x59}, {'Z', 0x5A}, {'[', 0x5B}, {'\\', 0x5C}, {']', 0x5D}, {'^', 0x5E}, {'_', 0x5F}, 104 | {'`', 0x60}, {193, 0x61}, {195, 0x62}, {129, 0x63}, {131, 0x63}, {196, 0x64}, {168, 0x65}, {198, 0x66}, {199, 0x67}, {200, 0x68}, {201, 0x69}, {203, 0x6A}, {207, 0x6B}, {211, 0x6C}, {212, 0x6D}, {214, 0x6E}, {215, 0x6F}, 105 | {216, 0x70}, {217, 0x71}, {218, 0x72}, {219, 0x73}, {220, 0x74}, {221, 0x75}, {222, 0x76}, {223, 0x77}, {142, 0x78}, {170, 0x79}, {175, 0x7A}, {'{', 0x7B}, {'|', 0x7C}, {'}', 0x7D}, {'~', 0x7E}, 106 | 107 | // 2 - Alphanumeric Latin/Cyrillic (Philips PRG2220, PRG2310) 108 | {'@', 0x40}, {'A', 0x41}, {'B', 0x42}, {'C', 0x43}, {'D', 0x44}, {'E', 0x45}, {'F', 0x46}, {'G', 0x47}, {'H', 0x48}, {'I', 0x49}, {'J', 0x4A}, {'K', 0x4B}, {'L', 0x4C}, {'M', 0x4D}, {'N', 0x4E}, {'O', 0x4F}, 109 | {'P', 0x50}, {'Q', 0x51}, {'R', 0x52}, {'S', 0x53}, {'T', 0x54}, {'U', 0x55}, {'V', 0x56}, {'W', 0x57}, {'X', 0x58}, {'Y', 0x59}, {'Z', 0x5A}, {'[', 0x5B}, {'\\', 0x5C}, {']', 0x5D}, {'^', 0x5E}, {'_', 0x5F}, 110 | {192, 0x60}, {193, 0x61}, {194, 0x62}, {195, 0x63}, {196, 0x64}, {197, 0x65}, {168, 0x65}, {198, 0x66}, {199, 0x67}, {200, 0x68}, {201, 0x69}, {202, 0x6A}, {203, 0x6B}, {204, 0x6C}, {205, 0x6D}, {206, 0x6E}, {207, 0x6F}, 111 | {208, 0x70}, {209, 0x71}, {210, 0x72}, {211, 0x73}, {212, 0x74}, {213, 0x75}, {214, 0x76}, {215, 0x77}, {216, 0x78}, {217, 0x79}, {218, 0x7A}, {219, 0x7B}, {220, 0x7C}, {221, 0x7D}, {222, 0x7E}, {223, 0x7F}, 112 | 113 | // 3 - Alphanumeric Cyrillic (Motorola Advisor, Scriptor LX1) 114 | {254, 0x40}, {224, 0x41}, {225, 0x42}, {246, 0x43}, {228, 0x44}, {229, 0x45}, {184, 0x45}, {244, 0x46}, {227, 0x47}, {245, 0x48}, {232, 0x49}, {233, 0x4A}, {234, 0x4B}, {235, 0x4C}, {236, 0x4D}, {237, 0x4E}, {238, 0x4F}, 115 | {239, 0x50}, {255, 0x51}, {240, 0x52}, {241, 0x53}, {242, 0x54}, {243, 0x55}, {230, 0x56}, {226, 0x57}, {250, 0x58}, {252, 0x58}, {251, 0x59}, {231, 0x5A}, {248, 0x5B}, {253, 0x5C}, {249, 0x5D}, {247, 0x5E}, {'_', 0x5F}, 116 | {222, 0x60}, {192, 0x61}, {193, 0x62}, {214, 0x63}, {196, 0x64}, {197, 0x65}, {168, 0x65}, {212, 0x66}, {195, 0x67}, {213, 0x68}, {200, 0x69}, {201, 0x6A}, {202, 0x6B}, {203, 0x6C}, {204, 0x6D}, {205, 0x6E}, {206, 0x6F}, 117 | {207, 0x70}, {223, 0x71}, {208, 0x72}, {209, 0x73}, {210, 0x74}, {211, 0x75}, {198, 0x76}, {194, 0x77}, {218, 0x78}, {220, 0x78}, {219, 0x79}, {199, 0x7A}, {216, 0x7B}, {221, 0x7C}, {217, 0x7D}, {215, 0x7E} 118 | }; 119 | 120 | #endif /* _POCSAGENCODER_H_ */ 121 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Cmd.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Copyright (C) 2009 FreakLabs 3 | All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the the copyright holder nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | THIS SOFTWARE IS PROVIDED BY THE THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | SUCH DAMAGE. 26 | Originally written by Christopher Wang aka Akiba. 27 | Please post support questions to the FreakLabs forum. 28 | *******************************************************************/ 29 | /*! 30 | \file Cmd.c 31 | This implements a simple command line interface for the Arduino so that 32 | its possible to execute individual functions within the sketch. 33 | */ 34 | /**************************************************************************/ 35 | #include 36 | #if ARDUINO >= 100 37 | #include 38 | #else 39 | #include 40 | #endif 41 | #include "HardwareSerial.h" 42 | #include "Cmd.h" 43 | 44 | //#define DEBUG 45 | 46 | // command line message buffer and pointer 47 | static uint8_t msg[MAX_MSG_SIZE]; 48 | static uint8_t *msg_ptr; 49 | 50 | 51 | // linked list for command table 52 | static cmd_t *cmd_tbl_list, *cmd_tbl; 53 | 54 | // text strings for command prompt (stored in flash) 55 | const char cmd_banner[] PROGMEM = "*************** CMD *******************"; 56 | const char cmd_prompt[] PROGMEM = "CMD >> "; 57 | const char cmd_unrecog[] PROGMEM = "What?"; 58 | 59 | byte canRun = 1; 60 | uint32_t speedUart; 61 | 62 | /**************************************************************************/ 63 | /*! 64 | Generate the main command prompt 65 | */ 66 | /**************************************************************************/ 67 | void cmd_display() 68 | { 69 | char buf[50]; 70 | // strcpy_P(buf, cmd_banner); 71 | //strcpy_P(buf, cmd_prompt); 72 | 73 | #ifdef DEBUG 74 | Serial.print(buf); 75 | Serial.println(); 76 | Serial.println(buf); 77 | #endif 78 | } 79 | 80 | /**************************************************************************/ 81 | /*! 82 | Parse the command line. This function tokenizes the command input, then 83 | searches for the command table entry associated with the commmand. Once found, 84 | it will jump to the corresponding function. 85 | */ 86 | /**************************************************************************/ 87 | void cmd_parse(char *cmd) 88 | { 89 | uint8_t argc, i = 0; 90 | char *argv[30]; 91 | char buf[50]; 92 | cmd_t *cmd_entry; 93 | 94 | fflush(stdout); 95 | 96 | // parse the command line statement and break it up into space-delimited 97 | // strings. the array of strings will be saved in the argv array. 98 | argv[i] = strtok(cmd, " "); 99 | do 100 | { 101 | argv[++i] = strtok(NULL, " "); 102 | } while ((i < 30) && (argv[i] != NULL)); 103 | 104 | // save off the number of arguments for the particular command. 105 | argc = i; 106 | 107 | // parse the command table for valid command. used argv[0] which is the 108 | // actual command name typed in at the prompt 109 | for (cmd_entry = cmd_tbl; cmd_entry != NULL; cmd_entry = cmd_entry->next) 110 | { 111 | if (!strcmp(argv[0], cmd_entry->cmd)) 112 | { 113 | cmd_entry->func(argc, argv); 114 | cmd_display(); 115 | return; 116 | } 117 | } 118 | 119 | // command not recognized. print message and re-generate prompt. 120 | strcpy_P(buf, cmd_unrecog); 121 | #ifdef DEBUG 122 | Serial.println(buf); 123 | #endif 124 | 125 | cmd_display(); 126 | } 127 | 128 | /**************************************************************************/ 129 | /*! 130 | This function processes the individual characters typed into the command 131 | prompt. It saves them off into the message buffer unless its a "backspace" 132 | or "enter" key. 133 | */ 134 | /**************************************************************************/ 135 | void cmd_handler() 136 | { 137 | char c = Serial.read(); 138 | 139 | switch (c) 140 | { 141 | case '\r': 142 | // terminate the msg and reset the msg ptr. then send 143 | // it to the handler for processing. 144 | *msg_ptr = '\0'; 145 | #ifdef DEBUG 146 | Serial.print("\r\n"); 147 | #endif 148 | cmd_parse((char *)msg); 149 | msg_ptr = msg; 150 | break; 151 | 152 | case '\b': 153 | // backspace 154 | #ifdef DEBUG 155 | Serial.print(c); 156 | #endif 157 | if (msg_ptr > msg) 158 | { 159 | msg_ptr--; 160 | } 161 | break; 162 | 163 | default: 164 | // normal character entered. add it to the buffer 165 | #ifdef DEBUG 166 | Serial.print(c); 167 | #endif 168 | *msg_ptr++ = c; 169 | break; 170 | } 171 | } 172 | 173 | /**************************************************************************/ 174 | /*! 175 | This function should be set inside the main loop. It needs to be called 176 | constantly to check if there is any available input at the command prompt. 177 | */ 178 | /**************************************************************************/ 179 | void cmdPoll() 180 | { 181 | while ( Serial.available()) 182 | { 183 | cmd_handler(); 184 | } 185 | } 186 | 187 | void clearCmd() { 188 | cmd_t* p, *next; 189 | for (p = cmd_tbl_list; p != NULL; p = next) { 190 | next = p->next; 191 | free(p->cmd); 192 | free(p); 193 | } 194 | cmd_tbl_list=NULL; 195 | } 196 | 197 | /**************************************************************************/ 198 | /*! 199 | Initialize the command line interface. This sets the terminal speed and 200 | and initializes things. 201 | */ 202 | /**************************************************************************/ 203 | void cmdInit(uint32_t speed) 204 | { 205 | // init the msg ptr 206 | speedUart = speed; 207 | msg_ptr = msg; 208 | 209 | // init the command table 210 | cmd_tbl_list = NULL; 211 | 212 | // set the serial speed 213 | Serial.begin(speed); 214 | } 215 | 216 | /**************************************************************************/ 217 | /*! 218 | Add a command to the command table. The commands should be added in 219 | at the setup() portion of the sketch. 220 | */ 221 | /**************************************************************************/ 222 | void cmdAdd(char *name, void (*func)(int argc, char **argv)) 223 | { 224 | // alloc memory for command struct 225 | cmd_tbl = (cmd_t *)malloc(sizeof(cmd_t)); 226 | 227 | // alloc memory for command name 228 | char *cmd_name = (char *)malloc(strlen(name) + 1); 229 | 230 | // copy command name 231 | strcpy(cmd_name, name); 232 | 233 | // terminate the command name 234 | cmd_name[strlen(name)] = '\0'; 235 | 236 | // fill out structure 237 | cmd_tbl->cmd = cmd_name; 238 | cmd_tbl->func = func; 239 | cmd_tbl->next = cmd_tbl_list; 240 | cmd_tbl_list = cmd_tbl; 241 | } 242 | 243 | void pauseCmd(byte pause) { 244 | canRun = pause; 245 | } 246 | 247 | /**************************************************************************/ 248 | /*! 249 | Convert a string to a number. The base must be specified, ie: "32" is a 250 | different value in base 10 (decimal) and base 16 (hexadecimal). 251 | */ 252 | /**************************************************************************/ 253 | uint32_t cmdStr2Num(char *str, uint8_t base) 254 | { 255 | return strtol(str, NULL, base); 256 | } 257 | -------------------------------------------------------------------------------- /TelePager-bot/.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/PocsagEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "PocsagEncoder.h" 2 | 3 | PocsagEncoder::PocsagEncoder() { 4 | } 5 | 6 | void PocsagEncoder::setCapCode(uint32_t capCode) { 7 | this->capCode = capCode; 8 | } 9 | 10 | void PocsagEncoder::setSource(byte source) { 11 | this->source = source; 12 | } 13 | 14 | void PocsagEncoder::setEncodingId(byte encodingId) { 15 | this->encodingId = encodingId; 16 | } 17 | 18 | void PocsagEncoder::setPrintMessage(byte printMessage) { 19 | this->printMessage = printMessage; 20 | } 21 | 22 | static word calculateBCH(uint32_t cw) { 23 | uint32_t generator = 0xED200000; 24 | uint32_t mask = 0x80000000; 25 | 26 | for (byte i = 0; i < 21; i++) { 27 | if (cw & mask) { 28 | cw ^= generator; 29 | } 30 | generator >>= 1; 31 | mask >>= 1; 32 | } 33 | 34 | return cw >> 1; 35 | } 36 | 37 | static byte calculateParity(uint32_t cw) { 38 | byte parity = 0; 39 | 40 | for (byte i = 0; i < 32; i++) { 41 | if (cw & 0x80000000) { 42 | parity ^= 1; 43 | } 44 | cw <<= 1; 45 | } 46 | 47 | return parity; 48 | } 49 | 50 | static void flipEndianess(uint32_t *value) { 51 | uint8_t *bytes = (uint8_t*) value; 52 | uint8_t tmp = bytes[0]; 53 | bytes[0] = bytes[3]; 54 | bytes[3] = tmp; 55 | tmp = bytes[1]; 56 | bytes[1] = bytes[2]; 57 | bytes[2] = tmp; 58 | } 59 | 60 | static void fillCW(PocsagEncoder::PocsagCW *codeWord, byte messageType, uint32_t payloadBits) { 61 | codeWord->data = 0; 62 | codeWord->messageType = messageType; 63 | codeWord->payloadBits = payloadBits; 64 | codeWord->crcBits = calculateBCH(codeWord->data); 65 | codeWord->parityBits = calculateParity(codeWord->data); 66 | flipEndianess(&codeWord->data); 67 | } 68 | 69 | static void fillAddressCW(PocsagEncoder::PocsagCW *codeWord, uint32_t capCode, byte source) { 70 | fillCW(codeWord, 0, ((capCode >> 3) << 2) | source); 71 | } 72 | 73 | static void fillMessageCW(PocsagEncoder::PocsagCW *codeWord, uint32_t messageBits) { 74 | fillCW(codeWord, 1, messageBits); 75 | } 76 | 77 | // Fill preamble bytes with 0xAA and frames with idle code words, fill address code word and return first frame number 78 | static byte prepareMessage(PocsagEncoder::PocsagMessage *message, uint32_t capCode, byte source) { 79 | // Fill preamble 80 | for (byte i = 0; i < PCSG_PREAMBLE_LENGTH; i++) { 81 | message->preamble[i] = 0xAA; 82 | } 83 | // Fill frames with idle code words 84 | for (byte i = 0; i < PCSG_BATCHES_COUNT; i++) { 85 | message->batches[i].fsc = 0xD814D27C; // 0x7CD214D8 86 | for (byte j = 0; j < 8; j++) { 87 | message->batches[i].frames[j].codeWords[0].data = 0x97C1897A; // 0x7A89C197 88 | message->batches[i].frames[j].codeWords[1].data = 0x97C1897A; // 0x7A89C197 89 | } 90 | } 91 | 92 | // Determine first frame number 93 | byte currentFrame = capCode & 0x7; 94 | 95 | // Fill address code word 96 | fillAddressCW(&message->batches[0].frames[currentFrame].codeWords[0], capCode, source); 97 | 98 | return currentFrame; 99 | } 100 | 101 | // Convert 1-byte ASCII character to 5-bit Pocsag digit 102 | byte PocsagEncoder::getPocsagNumericCharacter(byte asciiChar) { 103 | for (byte i = 0; i < 16; i++) { 104 | if (pgm_read_byte(&PCSG_ALPHABET[i][0]) == asciiChar) { 105 | return pgm_read_byte(&PCSG_ALPHABET[i][1]); 106 | } 107 | } 108 | 109 | return 0xC; // ' ' (space) - unknown digit 110 | } 111 | 112 | // Convert 2-byte Latin or Cyrillic UTF-8 character to 1-byte Windows-1251 113 | static inline byte utf8ToWindows1251(word character, byte flipCase) { 114 | // Latin 115 | if (character <= 0x7E) { 116 | if (flipCase && character >= 0x41 && character <= 0x7A) { 117 | return character ^ (1 << 5); 118 | } 119 | return character; 120 | } 121 | 122 | // Cyrillic 123 | // Special cyrillic characters workaround 124 | switch(character) { 125 | case 0xD081: if (flipCase) return 184; else return 168; // Ё/ё 126 | case 0xD191: if (flipCase) return 168; else return 184; // ё/Ё 127 | case 0xD083: if (flipCase) return 131; else return 129; // Ѓ/ѓ 128 | case 0xD193: if (flipCase) return 129; else return 131; // ѓ/Ѓ 129 | case 0xD084: if (flipCase) return 186; else return 170; // Є/є 130 | case 0xD194: if (flipCase) return 170; else return 186; // є/Є 131 | case 0xD087: if (flipCase) return 191; else return 175; // Ї/ї 132 | case 0xD197: if (flipCase) return 175; else return 191; // ї/Ї 133 | case 0xD08B: if (flipCase) return 158; else return 142; // Ћ/ћ 134 | case 0xD19B: if (flipCase) return 142; else return 158; // ћ/Ћ 135 | default: break; 136 | } 137 | 138 | // Main cyrillic characters 139 | if (character >= 0xD090 && character <= 0xD18F) { 140 | if (character >= 0xD090 && character <= 0xD0BF) { 141 | // 'А' - 'п' 142 | character -= 0xCFD0; 143 | } else if (character >= 0xD180 && character <= 0xD18F) { 144 | // 'р' - 'я' 145 | character -= 0xD090; 146 | } 147 | if (flipCase) { 148 | return character ^ (1 << 5); 149 | } 150 | return character; 151 | } 152 | 153 | return 0x3F; // '?' - unknown symbol; 154 | } 155 | 156 | // Convert 2-byte Latin or Cyrillic UTF-8 character to 7-bit Pocsag character 157 | byte PocsagEncoder::getPocsagAlphanumericCharacter(word utf8Char, byte encodingId) { 158 | byte winChar = utf8ToWindows1251(utf8Char, 0); 159 | if (winChar == 0x3F) { 160 | return 0x3F; 161 | } 162 | 163 | // Search in common section 164 | for (byte i = 16; i < 48; i++) { 165 | if (pgm_read_byte(&PCSG_ALPHABET[i][0]) == winChar) { 166 | return pgm_read_byte(&PCSG_ALPHABET[i][1]); 167 | } 168 | } 169 | 170 | // Search in current case 171 | for (word i = PCSG_ENCODINGS[encodingId].offset; i < PCSG_ENCODINGS[encodingId].offset + PCSG_ENCODINGS[encodingId].length; i++) { 172 | if (pgm_read_byte(&PCSG_ALPHABET[i][0]) == winChar) { 173 | return pgm_read_byte(&PCSG_ALPHABET[i][1]); 174 | } 175 | } 176 | 177 | // Search in flipped case: 178 | winChar = utf8ToWindows1251(utf8Char, 1); 179 | for (word i = PCSG_ENCODINGS[encodingId].offset; i < PCSG_ENCODINGS[encodingId].offset + PCSG_ENCODINGS[encodingId].length; i++) { 180 | if (pgm_read_byte(&PCSG_ALPHABET[i][0]) == winChar) { 181 | return pgm_read_byte(&PCSG_ALPHABET[i][1]); 182 | } 183 | } 184 | 185 | return 0x3F; // '?' - unknown symbol 186 | } 187 | 188 | static void printMsg(PocsagEncoder::PocsagMessage message) { 189 | byte byteCounter = 0; 190 | byte wordCounter = 0; 191 | uint32_t byteAcc = 0; 192 | for (word i = PCSG_PREAMBLE_LENGTH; i < message.messageLength; i++) { 193 | byteAcc <<= 8; 194 | byteAcc |= message.dataBytes[i]; 195 | if (++byteCounter >= 4) { 196 | Serial.print(byteAcc, HEX); 197 | if (++wordCounter % 8 == 1) { 198 | Serial.println(); 199 | } else { 200 | Serial.print(" "); 201 | if (wordCounter >= 18) { 202 | wordCounter = 1; 203 | Serial.println(); 204 | } 205 | } 206 | byteCounter = 0; 207 | byteAcc = 0; 208 | } 209 | } 210 | } 211 | 212 | static void incrementCounters(byte *currentBatch, byte *currentFrame, byte *currentCodeWord) { 213 | (*currentCodeWord)++; 214 | if (*currentCodeWord >= 2) { 215 | *currentCodeWord = 0; 216 | (*currentFrame)++; 217 | if (*currentFrame >= 8) { 218 | *currentFrame = 0; 219 | (*currentBatch)++; 220 | } 221 | } 222 | } 223 | 224 | static void pushPocsagChar(byte pocsagChar, byte charSize, uint32_t *messageBits, byte *messageBitsLength, 225 | PocsagEncoder::PocsagMessage *message, byte *currentBatch, byte *currentFrame, byte *currentCodeWord) { 226 | for (byte b = 0; b < charSize; b++) { 227 | *messageBits |= pocsagChar & 1; 228 | (*messageBitsLength)++; 229 | 230 | // Push message bits to code word 231 | if (*messageBitsLength >= 20) { 232 | *messageBitsLength = 0; 233 | fillMessageCW(&message->batches[*currentBatch].frames[*currentFrame].codeWords[*currentCodeWord], *messageBits); 234 | incrementCounters(currentBatch, currentFrame, currentCodeWord); 235 | } 236 | 237 | *messageBits <<= 1; 238 | pocsagChar >>= 1; 239 | } 240 | } 241 | 242 | PocsagEncoder::PocsagMessage PocsagEncoder::encodeNumeric(String messageText) { 243 | PocsagMessage message; 244 | 245 | // Prepare message and fill address CW 246 | byte currentFrame = prepareMessage(&message, capCode, source); 247 | byte currentBatch = 0; 248 | byte currentCodeWord = 1; 249 | uint32_t messageBits = 0; 250 | byte messageBitsLength = 0; 251 | 252 | // Convert and push all characters to message code words 253 | for (word i = 0; i < messageText.length(); i++) { 254 | byte nextChar = messageText.charAt(i); 255 | 256 | // Skip leading space and non-printable symbols 257 | if ((i == 0 && nextChar == 0x20) || nextChar < 0x20) { 258 | continue; 259 | } 260 | 261 | // Translate to pocsag digit 262 | byte pocsagChar = getPocsagNumericCharacter(nextChar); 263 | 264 | // Push next digit to message bits 265 | pushPocsagChar(pocsagChar, 4, &messageBits, &messageBitsLength, &message, ¤tBatch, ¤tFrame, ¤tCodeWord); 266 | } 267 | 268 | // Fill empty space of last message codeword with 0xC character 269 | if (messageBitsLength > 0 && messageBitsLength < 20) { 270 | byte freeSpace = 20 - messageBitsLength; 271 | for (byte i = 0; i < freeSpace / 4; i++) { 272 | pushPocsagChar(0xC, 4, &messageBits, &messageBitsLength, &message, ¤tBatch, ¤tFrame, ¤tCodeWord); 273 | } 274 | } 275 | 276 | message.messageLength = PCSG_PREAMBLE_LENGTH + 68 * (currentBatch + 1); 277 | 278 | if (printMessage) { 279 | printMsg(message); 280 | } 281 | 282 | return message; 283 | } 284 | 285 | PocsagEncoder::PocsagMessage PocsagEncoder::encodeAlphanumeric(String messageText) { 286 | PocsagMessage message; 287 | 288 | // Prepare message and fill address CW 289 | byte currentFrame = prepareMessage(&message, capCode, source); 290 | byte currentBatch = 0; 291 | byte currentCodeWord = 1; 292 | uint32_t messageBits = 0; 293 | byte messageBitsLength = 0; 294 | 295 | // Convert and push all characters to message code words 296 | for (word i = 0; i < messageText.length(); i++) { 297 | word nextChar = messageText.charAt(i); 298 | 299 | // Skip leading space and non-printable symbols 300 | if ((i == 0 && nextChar == 0x20) || nextChar < 0x20) { 301 | continue; 302 | } 303 | 304 | // 2-byte UTF-8 symbol - read second byte (workaround for Cyrillic characters) 305 | if (nextChar & 0x80) { 306 | i++; 307 | nextChar <<= 8; 308 | nextChar |= (byte) messageText.charAt(i); 309 | } 310 | 311 | // Translate to pocsag alphabet using current encoding 312 | byte pocsagChar = getPocsagAlphanumericCharacter(nextChar, encodingId); 313 | 314 | // Check if there is enough space to put next char in the last code word of the last batch 315 | // We must preserve at least 7 bits to put EOT/ETX character at the end of the message 316 | if (currentBatch == PCSG_BATCHES_COUNT - 1 && currentFrame == 7 && currentCodeWord == 1) { 317 | // Skip all remaining chars because they won't fit 318 | if (messageBitsLength >= 7) { 319 | break; 320 | } 321 | } 322 | 323 | // Push next character to message bits 324 | pushPocsagChar(pocsagChar, 7, &messageBits, &messageBitsLength, &message, ¤tBatch, ¤tFrame, ¤tCodeWord); 325 | } 326 | 327 | // Push EOT symbol to the end of the message 328 | byte eotChar = PCSG_EOT_CHAR; 329 | pushPocsagChar(eotChar, 7, &messageBits, &messageBitsLength, &message, ¤tBatch, ¤tFrame, ¤tCodeWord); 330 | 331 | // Fill last message code word 332 | if (messageBitsLength > 0 && messageBitsLength < 20) { 333 | // Try to push additional EOTs 334 | byte freeSpace = 20 - messageBitsLength; 335 | for (byte i = 0; i < freeSpace / 7; i++) { 336 | pushPocsagChar(eotChar, 7, &messageBits, &messageBitsLength, &message, ¤tBatch, ¤tFrame, ¤tCodeWord); 337 | } 338 | // Trying to fill left empty space with EOT bits partially 339 | for (byte b = 0; b < 19 - messageBitsLength; b++) { 340 | messageBits |= eotChar & 1; 341 | messageBits <<= 1; 342 | eotChar >>= 1; 343 | } 344 | fillMessageCW(&message.batches[currentBatch].frames[currentFrame].codeWords[currentCodeWord], messageBits); 345 | } 346 | 347 | message.messageLength = PCSG_PREAMBLE_LENGTH + 68 * (currentBatch + 1); 348 | 349 | if (printMessage) { 350 | printMsg(message); 351 | } 352 | 353 | return message; 354 | } 355 | 356 | PocsagEncoder::PocsagMessage PocsagEncoder::encodeTone() { 357 | PocsagMessage message; 358 | 359 | // Fill address CW only 360 | prepareMessage(&message, capCode, source); 361 | message.messageLength = PCSG_PREAMBLE_LENGTH + 68; 362 | 363 | if (printMessage) { 364 | printMsg(message); 365 | } 366 | 367 | return message; 368 | } 369 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/Rf7021.cpp: -------------------------------------------------------------------------------- 1 | #include "rf7021.h" 2 | 3 | Rf7021::Rf7021() { 4 | // Default settings 5 | rfConfig.xtalFrequency = 19680000UL; // Default 19.68 MHz TCXO 6 | rfConfig.hasExternalInductor = 1; // RF7021SE has no external inductor on L1-L2 pins by default 7 | rfConfig.xtalBias = 3; // 0 - 20uA, 1 - 25uA, 2 - 30uA, 3 - 35uA 8 | rfConfig.cpCurrent = 3; // 0 - 0.3mA, 1 - 0.9mA, 2 - 1.5mA, 3 - 2.1mA 9 | rfConfig.modulationScheme = 0; // 0 - FSK, 1 - GFSK, 2 - 3FSK, 3 - 4FSK, 4 - OFSK, 5 - RCFSK, 6 - RC3FSK, 7 - RC4FSK 10 | rfConfig.powerAmplifierEnabled = 1; // 0 - OFF, 1 - ON 11 | rfConfig.powerAmplifierRamping = 1; // 0 - OFF, 1 - LOWEST, ..., 7 - HIGHEST 12 | rfConfig.powerAmplifierBias = 3; // 0 - 5uA, 1 - 7uA, 2 - 9uA, 3 - 11 uA 13 | rfConfig.powerAmplifierPower = 63; // 0 - OFF, 1 - LOWEST, ..., 63 - HIGHEST 14 | rfConfig.dataInvertType = 2; // 0 - NONE, 1 - Invert CLK, 2 - Invert DATA, 3 - Invert CLK and DATA 15 | rfConfig.dataInvertEnabled = 0; // 0 - OFF, 1 - ON 16 | rfConfig.rCosineAlpha = 1; // 0 - 0.5, 1 - 0.7 17 | } 18 | 19 | void Rf7021::setTxRxDataPin(byte txRxDataPin) { 20 | rfConfig.txRxDataPin = txRxDataPin; 21 | pinMode(txRxDataPin, OUTPUT); 22 | } 23 | 24 | void Rf7021::setTxRxCLKPin(byte txRxCLKPin) { 25 | rfConfig.txRxCLKPin = txRxCLKPin; 26 | pinMode(txRxCLKPin, INPUT); 27 | } 28 | 29 | void Rf7021::setCEPin(byte cePin) { 30 | rfConfig.cePin = cePin; 31 | pinMode(cePin, OUTPUT); 32 | } 33 | 34 | void Rf7021::setSREADPin(byte sreadPin) { 35 | rfConfig.sreadPin = sreadPin; 36 | pinMode(sreadPin, INPUT); 37 | } 38 | 39 | void Rf7021::setSLEPin(byte slePin) { 40 | rfConfig.slePin = slePin; 41 | pinMode(slePin, OUTPUT); 42 | } 43 | 44 | void Rf7021::setSDATAPin(byte sdataPin) { 45 | rfConfig.sdataPin = sdataPin; 46 | pinMode(sdataPin, OUTPUT); 47 | } 48 | 49 | void Rf7021::setSCLKPin(byte sclkPin) { 50 | rfConfig.sclkPin = sclkPin; 51 | pinMode(sclkPin, OUTPUT); 52 | } 53 | 54 | void Rf7021::setXtalFrequency(uint32_t xtalFrequency) { 55 | rfConfig.xtalFrequency = xtalFrequency; 56 | } 57 | 58 | uint32_t Rf7021::getXtalFrequency() { 59 | return rfConfig.xtalFrequency; 60 | } 61 | 62 | void Rf7021::setXtalBias(byte xtalBias) { 63 | rfConfig.xtalBias = xtalBias; 64 | } 65 | 66 | void Rf7021::setCpCurrent(byte cpCurrent) { 67 | rfConfig.cpCurrent = cpCurrent; 68 | } 69 | 70 | void Rf7021::setHasExternalInductor(byte hasExternalInductor) { 71 | rfConfig.hasExternalInductor = hasExternalInductor; 72 | } 73 | 74 | void Rf7021::setmModulationScheme(byte modulationScheme) { 75 | rfConfig.modulationScheme = modulationScheme; 76 | } 77 | 78 | void Rf7021::setPowerAmplifierEnabled(byte powerAmplifierEnabled) { 79 | rfConfig.powerAmplifierEnabled = powerAmplifierEnabled; 80 | } 81 | 82 | void Rf7021::setPowerAmplifierRamping(byte powerAmplifierRamping) { 83 | rfConfig.powerAmplifierRamping = powerAmplifierRamping; 84 | } 85 | 86 | void Rf7021::setPowerAmplifierBias(byte powerAmplifierBias) { 87 | rfConfig.powerAmplifierBias = powerAmplifierBias; 88 | } 89 | 90 | void Rf7021::setPowerAmplifierPower(byte powerAmplifierPower) { 91 | rfConfig.powerAmplifierPower = powerAmplifierPower; 92 | } 93 | 94 | void Rf7021::setDataInvertType(byte dataInvertType) { 95 | rfConfig.dataInvertType = dataInvertType; 96 | } 97 | 98 | void Rf7021::setRCosineAlpha(byte rCosineAlpha) { 99 | rfConfig.rCosineAlpha = rCosineAlpha; 100 | } 101 | 102 | void Rf7021::setDataRate(word dataRate) { 103 | rfConfig.dataRate = dataRate; 104 | } 105 | 106 | void Rf7021::setFrequency(uint32_t frequency) { 107 | rfConfig.frequency = frequency; 108 | } 109 | 110 | uint32_t Rf7021::getFrequency() { 111 | return rfConfig.frequency ; 112 | } 113 | 114 | void Rf7021::setFrequencyDeviation(word frequencyDeviation) { 115 | rfConfig.frequencyDeviation = frequencyDeviation; 116 | } 117 | 118 | void Rf7021::setDataInvertEnabled(byte dataInvertEnabled) { 119 | rfConfig.dataInvertEnabled = dataInvertEnabled; 120 | } 121 | 122 | void Rf7021::writeReg(RfReg *reg) { 123 | digitalWrite(rfConfig.slePin, LOW); 124 | digitalWrite(rfConfig.sclkPin, LOW); 125 | 126 | for (byte i = 4; i > 0; i--) { 127 | byte buf = reg->dataBytes[i - 1]; 128 | for (byte j = 8; j > 0; j--) { 129 | digitalWrite(rfConfig.sclkPin, LOW); 130 | digitalWrite(rfConfig.sdataPin, (buf & 0x80) ? HIGH : LOW); 131 | digitalWrite(rfConfig.sclkPin, HIGH); 132 | buf <<= 1; 133 | } 134 | digitalWrite(rfConfig.sclkPin, LOW); 135 | } 136 | 137 | digitalWrite(rfConfig.slePin, HIGH); 138 | delay(1); 139 | digitalWrite(rfConfig.sdataPin, LOW); 140 | digitalWrite(rfConfig.slePin, LOW); 141 | } 142 | 143 | Rf7021::RfReg Rf7021::readReg(word readbackConfig) { 144 | RfReg reg; 145 | reg.data = ((readbackConfig & 0x1F) << 4); 146 | 147 | reg.data |= 7; 148 | writeReg(®); 149 | reg.data = 0; 150 | 151 | digitalWrite(rfConfig.sdataPin, LOW); 152 | digitalWrite(rfConfig.sclkPin, LOW); 153 | digitalWrite(rfConfig.slePin, HIGH); 154 | 155 | // Skip first DB16 byte 156 | digitalWrite(rfConfig.sclkPin, HIGH); 157 | digitalWrite(rfConfig.sclkPin, LOW); 158 | 159 | byte buf = 0; 160 | for (byte i = 2; i > 0; i--) { 161 | for (byte j = 8; j > 0; j--) { 162 | digitalWrite(rfConfig.sclkPin, HIGH); 163 | buf <<= 1; 164 | if (digitalRead(rfConfig.sreadPin)) { 165 | buf |= 1; 166 | } 167 | digitalWrite(rfConfig.sclkPin, LOW); 168 | } 169 | reg.dataBytes[i - 1] = buf; 170 | } 171 | 172 | digitalWrite(rfConfig.sclkPin, HIGH); 173 | digitalWrite(rfConfig.slePin, LOW); 174 | digitalWrite(rfConfig.sclkPin, LOW); 175 | 176 | return reg; 177 | } 178 | 179 | word Rf7021::getSiliconRev() { 180 | powerOn(); 181 | word value = readReg(0x1C).dataLower; 182 | powerOff(); 183 | return value; 184 | } 185 | 186 | float Rf7021::getTemp() { 187 | powerOn(); 188 | RfReg reg; 189 | reg.data = 8; 190 | reg.data |= 1 << 8; 191 | writeReg(®); 192 | reg = readReg(0x16); 193 | powerOff(); 194 | return 469.5 - (7.2 * (reg.dataBytes[0] & 0x7F)); 195 | } 196 | 197 | float Rf7021::getVoltage() { 198 | powerOn(); 199 | RfReg reg; 200 | reg.data = 8; 201 | reg.data |= 1 << 8; 202 | writeReg(®); 203 | reg = readReg(0x15); 204 | powerOff(); 205 | return (reg.dataBytes[0] & 0x7F) / 21.1; 206 | } 207 | 208 | void Rf7021::powerOn() { 209 | digitalWrite(rfConfig.cePin, HIGH); 210 | delay(1); 211 | 212 | // Prepare and push Register 1 213 | rfConfig.R1.addressBits = 1; 214 | rfConfig.R1.rCounter = 1; 215 | rfConfig.R1.clockoutDivide = 0; 216 | rfConfig.R1.xtalDoubler = 0; 217 | rfConfig.R1.xoscEnable = 1; 218 | rfConfig.R1.xtalBias = rfConfig.xtalBias; 219 | rfConfig.R1.cpCurrent = rfConfig.cpCurrent; 220 | rfConfig.R1.vcoEnable = 1; 221 | if (rfConfig.frequency >= 900000000UL && rfConfig.frequency <= 950000000UL) { 222 | // 900 - 950 MHz internal VCO 223 | rfConfig.R1.rfDivideBy2 = 0; 224 | rfConfig.R1.vcoBias = 8; 225 | rfConfig.R1.vcoAdjust = 3; 226 | rfConfig.R1.vcoInductor = 0; 227 | } else if (rfConfig.frequency >= 800000000UL && rfConfig.frequency < 900000000UL) { 228 | // 862 - 900 MHz internal VCO 229 | rfConfig.R1.rfDivideBy2 = 0; 230 | rfConfig.R1.vcoBias = 8; 231 | rfConfig.R1.vcoAdjust = 0; 232 | rfConfig.R1.vcoInductor = 0; 233 | } else if (rfConfig.frequency >= 450000000UL && rfConfig.frequency < 500000000UL) { 234 | // 450 - 500 MHz internal VCO 235 | rfConfig.R1.rfDivideBy2 = 1; 236 | rfConfig.R1.vcoBias = 8; 237 | rfConfig.R1.vcoAdjust = 3; 238 | rfConfig.R1.vcoInductor = 0; 239 | } else if (rfConfig.frequency >= 400000000UL && rfConfig.frequency < 450000000UL) { 240 | // 400 - 450 MHz internal VCO 241 | rfConfig.R1.rfDivideBy2 = 1; 242 | rfConfig.R1.vcoBias = 8; 243 | rfConfig.R1.vcoAdjust = 0; 244 | rfConfig.R1.vcoInductor = 0; 245 | } else if (rfConfig.hasExternalInductor && rfConfig.frequency >= 500000000UL && rfConfig.frequency <= 650000000UL) { 246 | // 500 - 650 MHz external VCO 247 | rfConfig.R1.rfDivideBy2 = 0; 248 | rfConfig.R1.vcoBias = 4; 249 | rfConfig.R1.vcoInductor = 1; 250 | } else if (rfConfig.hasExternalInductor && rfConfig.frequency >= 200000000UL && rfConfig.frequency < 400000000UL) { 251 | // 200 - 400 MHz external VCO 252 | rfConfig.R1.rfDivideBy2 = 0; 253 | rfConfig.R1.vcoBias = 3; 254 | rfConfig.R1.vcoInductor = 1; 255 | } else if (rfConfig.hasExternalInductor && rfConfig.frequency >= 80000000UL && rfConfig.frequency < 200000000UL) { 256 | // 80 - 200 MHz external VCO 257 | rfConfig.R1.rfDivideBy2 = 1; 258 | rfConfig.R1.vcoBias = 2; 259 | rfConfig.R1.vcoInductor = 1; 260 | } 261 | writeReg(&rfConfig.r1); 262 | 263 | // Prepare and push Register 15 (CLK_MUX = 0x0 to enable default mode) 264 | rfConfig.R15.addressBits = 15; 265 | rfConfig.R15.rxTestMode = 0; 266 | rfConfig.R15.txTestMode = 0; 267 | rfConfig.R15.sdTestMode = 0; 268 | rfConfig.R15.cpTestMode = 0; 269 | rfConfig.R15.clkMux = 0; 270 | rfConfig.R15.pllTestMode = 0; 271 | rfConfig.R15.analogTestMode = 0; 272 | rfConfig.R15.forceLdHigh = 0; 273 | rfConfig.R15.reg1Pd = 0; 274 | rfConfig.R15.calOverride = 0; 275 | writeReg(&rfConfig.r15); 276 | 277 | // Prepare and push Register 3 278 | rfConfig.R3.addressBits = 3; 279 | // Find best BBOS divider 280 | for (byte bbosDiv = 0; bbosDiv < 4; bbosDiv++) { 281 | uint32_t bbosClk = round(rfConfig.xtalFrequency / (2 << (bbosDiv + 1))); 282 | if (bbosClk >= 1000000UL && bbosClk <= 2000000UL) { 283 | rfConfig.R3.bbosClkDivide = bbosDiv; 284 | } 285 | } 286 | // Find best DEMOD and CDR dividers to achieve required data rate 287 | double rateDeltaMin = rfConfig.dataRate; 288 | for (byte dem = 1; dem < 15; dem++) { 289 | byte cdr = round(rfConfig.xtalFrequency / (dem * rfConfig.dataRate * 32.0)); 290 | if (cdr == 0) continue; 291 | word realRate = round(rfConfig.xtalFrequency / (dem * cdr * 32.0)); 292 | double delta = abs(realRate - rfConfig.dataRate); 293 | if (delta < rateDeltaMin) { 294 | rfConfig.R3.demClkDivide = dem; 295 | rfConfig.R3.cdrClkDivide = cdr; 296 | rateDeltaMin = delta; 297 | } 298 | } 299 | rfConfig.R3.seqClkDivide = round(rfConfig.xtalFrequency / 100000.0); 300 | rfConfig.R3.agcClkDivide = round((rfConfig.R3.seqClkDivide * rfConfig.xtalFrequency) / 10000.0); 301 | writeReg(&rfConfig.r3); 302 | 303 | // Prepare and push Register 0 304 | rfConfig.R0.addressBits = 0; 305 | double n = (rfConfig.frequency * rfConfig.R1.rCounter / (rfConfig.xtalFrequency * (rfConfig.R1.rfDivideBy2 ? 0.5 : 1.0))); 306 | uint32_t nInt = floor(n); 307 | uint32_t nFrac = round((n - floor(n)) * 32768); 308 | rfConfig.R0.fractionalN = nFrac; 309 | rfConfig.R0.integerN = nInt; 310 | rfConfig.R0.rxMode = 0; 311 | rfConfig.R0.uartMode = 0; 312 | rfConfig.R0.muxOut = 0; 313 | writeReg(&rfConfig.r0); 314 | 315 | // Prepare and push Register 2 316 | rfConfig.R2.addressBits = 2; 317 | rfConfig.R2.modulationScheme = rfConfig.modulationScheme; 318 | rfConfig.R2.paEnable = rfConfig.powerAmplifierEnabled; 319 | rfConfig.R2.paRamp = rfConfig.powerAmplifierRamping; 320 | rfConfig.R2.paBias = rfConfig.powerAmplifierBias; 321 | rfConfig.R2.powerAmplifier = rfConfig.powerAmplifierPower; 322 | rfConfig.R2.txFrequencyDeviation = round(rfConfig.frequencyDeviation * 65536.0 * (rfConfig.R1.rfDivideBy2 ? 2.0 : 1.0) / ((double) rfConfig.xtalFrequency / rfConfig.R1.rCounter)); 323 | rfConfig.R2.txDataInvert = rfConfig.dataInvertEnabled ? rfConfig.dataInvertType : 0; 324 | rfConfig.R2.rcosineAlpha = rfConfig.rCosineAlpha; 325 | writeReg(&rfConfig.r2); 326 | } 327 | 328 | void Rf7021::powerOff() { 329 | digitalWrite(rfConfig.cePin, LOW); 330 | delay(1); 331 | } 332 | 333 | void Rf7021::txTest(byte testMode, word duration) { 334 | powerOn(); 335 | 336 | rfConfig.R15.txTestMode = testMode; 337 | writeReg(&rfConfig.r15); 338 | 339 | delay(duration); 340 | 341 | powerOff(); 342 | } 343 | 344 | bool Rf7021::sendMessage(byte *message, word messageLength) { 345 | powerOn(); 346 | 347 | byte bitCounter = 0; 348 | word byteCounter = 0; 349 | byte currentByte = message[0]; 350 | long startTime = millis(); 351 | 352 | while (byteCounter < messageLength) { 353 | // Wait LOW on TxRxCLK 354 | while (digitalRead(rfConfig.txRxCLKPin)); 355 | 356 | // Push bit 357 | if (currentByte & 0x80) { 358 | digitalWrite(rfConfig.txRxDataPin, HIGH); 359 | } else { 360 | digitalWrite(rfConfig.txRxDataPin, LOW); 361 | } 362 | 363 | // Switch to next bit 364 | bitCounter++; 365 | if (bitCounter >= 8) { 366 | bitCounter = 0; 367 | byteCounter++; 368 | currentByte = message[byteCounter]; 369 | } else { 370 | currentByte <<= 1; 371 | } 372 | 373 | // Wait HIGH on TxRxCLK 374 | while (!digitalRead(rfConfig.txRxCLKPin)) { 375 | if (millis() - startTime > 3000) { 376 | powerOff(); 377 | return false; 378 | } 379 | }; 380 | } 381 | 382 | powerOff(); 383 | return true; 384 | } 385 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/CmdProc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Rf7021.h" 3 | #include "Storage.h" 4 | #include "Cmd.h" 5 | #include 6 | 7 | Rf7021 rfD; 8 | 9 | String alignStr(char* strI, int len) { 10 | String ret = String(); 11 | String str = String(strI); 12 | int dif = len - str.length(); 13 | if (dif > 0) { 14 | int pre; 15 | 16 | if (dif % 2 == 0) 17 | pre = dif / 2; 18 | else 19 | pre = dif / 2 - 1 + dif % 2; 20 | 21 | for (int i = 0; i < dif; i++) { 22 | if (i == pre) 23 | ret += str; 24 | ret += (F(" ")); 25 | } 26 | 27 | } else { 28 | ret = str.substring(0, len); 29 | } 30 | return ret; 31 | } 32 | 33 | void pstr(String &str) { 34 | Serial.println(str); 35 | } 36 | 37 | void reserBuf() { 38 | while (Serial.available() > 0) { 39 | Serial.read(); 40 | } 41 | } 42 | 43 | String getCurStr() { 44 | return String(F(" / cur. ")); 45 | } 46 | void printSet(String val) { 47 | Serial.println(String(F("Set: ")) + val); 48 | } 49 | void printDiv() { 50 | Serial.println(F("\r--------------------------")); 51 | } 52 | 53 | 54 | 55 | 56 | void printWelcome(Rf7021 rf) { 57 | rfD = rf; 58 | Serial.println("\r\r\r\r\r"); 59 | Serial.println(F("-----===[ Welcome to Universal POCSAG Transmitter ]===-----")); 60 | Serial.print(F("Sysinfo: FW ver: 1.0, HW rev. ")); 61 | Serial.print(rf.getSiliconRev(), HEX); 62 | Serial.print(F(", temp.: ")); 63 | Serial.print(rf.getTemp()); 64 | Serial.print(F(", volt.: ")); 65 | Serial.println(rf.getVoltage()); 66 | Serial.println(F("\rSelect mode:")); 67 | Serial.println(F(" 1 - Free mode")); 68 | Serial.println(F(" 2 - Specified pager(s)")); 69 | Serial.println(F(" 3 - Saved pagers")); 70 | Serial.flush(); 71 | printDiv(); 72 | } 73 | 74 | 75 | /** 76 | * Меню базовых команд 77 | */ 78 | void printFreeMode() { 79 | // curPage = 1; 80 | Serial.println("\r\r\r\r\r"); 81 | Serial.println(F("-----===[ Free mode ]===-----")); 82 | Serial.println(F("Here you can set manual CAP, freq and other options")); 83 | Serial.println(F("Command \"send\" and base commands:")); 84 | Serial.println(F(" [f] - frequency, kHz")); 85 | Serial.println(F(" [c] - set CAP code")); 86 | Serial.println(F(" [r] - set data rate (512, 1200 or 2400)")); 87 | Serial.println(F(" [m] - text message, must be at the end of the prompt")); 88 | Serial.println(F("\r Example: \"send f 160218 c 123456 m Hello world!\"")); 89 | Serial.println(F("\r full - see all commands")); 90 | Serial.println(F(" 0 - go back")); 91 | printDiv(); 92 | } 93 | 94 | /** 95 | * Меню всех команд 96 | */ 97 | void printFreeModeAdv() { 98 | Serial.println(F("Advanced commands:")); 99 | Serial.println(F(" [n] - send numeric message (e.g. \"n U *123*456*789\")")); 100 | Serial.println(F(" [t] - send tone message")); 101 | Serial.println(F(" [d] - set frequency deviation in kHz")); 102 | Serial.println(F(" [i] - set data inversion (0 - disabled, 1 - enabled)")); 103 | Serial.println(F(" [s] - set message source code (1 to 3)")); 104 | Serial.println(F(" [e] - set encoding")); 105 | Serial.println(F(" 0 - EN Motorola")); 106 | Serial.println(F(" 1 - EN/RU Motorola")); 107 | Serial.println(F(" 2 - EN/RU Philips")); 108 | Serial.println(F(" 3 - RU Motorola")); 109 | Serial.println(F(" [x] - run transmiter test")); 110 | printDiv(); 111 | } 112 | 113 | 114 | 115 | /** 116 | * Вывод информации о пейджере 117 | */ 118 | void printPager(Pager *pager, bool detailed, bool wHeader) { 119 | // Serial.println(pager->cap); 120 | if (wHeader) { 121 | Serial.print(F("\r ID | Name | CAP |")); 122 | if (detailed) { 123 | Serial.print(F(" Freq | Rate | Source | Enc | Inv |")); 124 | } 125 | Serial.println(); 126 | for (int i = 0; i < (detailed ? 64 : 27); i++) { 127 | Serial.print("-"); 128 | } 129 | Serial.println(); 130 | } 131 | 132 | String det = ""; 133 | char buf[7]; 134 | char spc = ' '; 135 | char dev = '|'; 136 | 137 | ltoa(pager->addr, buf, DEC); 138 | det += alignStr(buf, 5) + dev; 139 | 140 | det += spc + alignStr(pager->alias, 10) + dev; 141 | 142 | ltoa(pager->cap, buf, DEC); 143 | det += spc + alignStr(buf, 7) + dev; 144 | 145 | 146 | if (detailed) { 147 | ltoa(pager->frequency, buf, DEC); 148 | det += spc + alignStr(buf, 6) + dev; 149 | 150 | ltoa(pager->rate, buf, DEC); 151 | det += spc + alignStr(buf, 5) + dev; 152 | 153 | ltoa(pager->msgSource, buf, DEC); 154 | det += spc + alignStr(buf, 7) + dev; 155 | 156 | ltoa(pager->enconding, buf, DEC); 157 | det += spc + alignStr(buf, 5) + dev; 158 | 159 | //ltoa(pager->inversion, buf, DEC); 160 | // det += spc + alignStr(buf, 4) + dev; 161 | buf[0] = pager->inversion==1?'y':'n'; 162 | det += spc + alignStr(buf, 4) + dev; 163 | } 164 | 165 | Serial.println(det); 166 | det = ""; 167 | } 168 | 169 | void printFixAdded(byte *arr) { 170 | Serial.print(F("Fixed IDs: ")); 171 | 172 | for (byte i = 0; i < STORAGE_COUNT; i++) { 173 | if (arr[i] < 0xff) { 174 | Serial.print(arr[i]); 175 | Serial.print(F(" ")); 176 | } 177 | } 178 | 179 | Serial.println(F(" ")); 180 | } 181 | 182 | /** 183 | * Меню отправки на сохраненные устройства 184 | */ 185 | void printSpecMode() { 186 | Serial.println(F("\r\r-----===[ Sending to saved devices ]===-----\r")); 187 | boolean header = true; 188 | struct Pager pager; 189 | for (byte i = 0; i < STORAGE_COUNT; i++) { 190 | getPager(i, &pager); 191 | 192 | if (pager.crc[0] == CRC[0] && pager.crc[1] == CRC[1]) { 193 | printPager(&pager, true, header); 194 | header = false; 195 | } 196 | } 197 | memset(&pager, 0, sizeof(Pager)); 198 | Serial.println(F("\r to [ID ID] [send [text], numeric [0-9,-,*,\" \",(,)], tone] - Send message")); 199 | Serial.println(F(" to selected IDs")); 200 | Serial.println(F(" fix [ID ID] - select IDs (then just \"send [text]\")")); 201 | Serial.println(F(" 0 - go back")); 202 | 203 | Serial.println(String(F("\r Example: \"to 0 4 12 7 send Hello! Get out of here!\""))); 204 | Serial.println(String(F(" \"to 4 12 numeric 8-800-2000-600\""))); 205 | Serial.println(String(F(" \"to 3 5 tone\""))); 206 | printDiv(); 207 | } 208 | 209 | /** 210 | * Меню операций с хранилищем 211 | */ 212 | void printListMode() { 213 | Serial.println(F("\r\r\r-----===[ Stored list mode ]===-----\r")); 214 | 215 | boolean header = true; 216 | struct Pager pager; 217 | for (byte i = 0; i < STORAGE_COUNT; i++) { 218 | getPager(i, &pager); 219 | if (pager.crc[0] == CRC[0] && pager.crc[1] == CRC[1]) { 220 | printPager(&pager, true, header); 221 | header = false; 222 | } 223 | } 224 | 225 | Serial.println(F("\r add - add device")); 226 | Serial.println(F(" edit [ID] - edit device")); 227 | Serial.println(F(" del [ID] - remove device")); 228 | Serial.println(F(" 0 - go back")); 229 | 230 | printDiv(); 231 | } 232 | 233 | /** 234 | * Функция добавления/редактирования пейджера 235 | */ 236 | void printAddDevice(byte stepC, Pager* pager) { //stepC - for enter parametr 237 | if (pager->addr == 0xff) { 238 | // Create default params 239 | 240 | pager->cap = 0; 241 | pager->frequency = 0; 242 | pager->msgSource = 0; 243 | pager->enconding = 4; 244 | pager->inversion = 0; 245 | pager->rate = 1200; 246 | } 247 | int srs; 248 | long srsL; 249 | String arg = ""; 250 | switch (stepC) { 251 | case 0: { 252 | reserBuf(); 253 | if (pager->addr != 0xff) { 254 | arg = getCurStr() + pager->addr; 255 | } 256 | Serial.print(F("Enter ID (0-20)")); 257 | Serial.println(arg); 258 | 259 | while (Serial.available() == 0) { 260 | } 261 | 262 | if (pager->addr != 0xff && Serial.peek() == '\r') { 263 | printSet(String(pager->addr)); 264 | printAddDevice(1, pager); 265 | return; 266 | } 267 | 268 | srs = Serial.parseInt(); 269 | 270 | if (srs > 20 | srs < 0) { 271 | Serial.println(F("Incorrect data")); 272 | printAddDevice(0, pager); 273 | } else { 274 | pager->addr = srs; 275 | printSet(String(srs)); 276 | printAddDevice(1, pager); 277 | } 278 | 279 | return; 280 | } 281 | case 1: { 282 | reserBuf(); 283 | if (pager->cap > 0) { 284 | arg = getCurStr() + pager->cap; 285 | } 286 | Serial.print(F("Enter CAP")); 287 | Serial.println(arg); 288 | 289 | while (Serial.available() == 0) { 290 | } 291 | 292 | 293 | if (pager->cap > 0 && Serial.peek() == '\r') { 294 | printSet(String(pager->cap)); 295 | printAddDevice(2, pager); 296 | return; 297 | } 298 | 299 | srsL = Serial.parseInt(); 300 | 301 | if (srsL < 100 | srsL > 9999999) { 302 | Serial.println(F("Incorrect data")); 303 | printAddDevice(1, pager); 304 | } else { 305 | pager->cap = srsL; 306 | } 307 | 308 | printSet(String(pager->cap)); 309 | printAddDevice(2, pager); 310 | 311 | return; 312 | } 313 | 314 | case 2: { 315 | reserBuf(); 316 | Serial.print(F("Enter name (max 10 symbols)")); 317 | if (pager->alias[0] > 0) { 318 | arg = getCurStr() + pager->alias; 319 | } 320 | Serial.println(arg); 321 | 322 | while (Serial.available() == 0) { 323 | } 324 | 325 | if (pager->alias[0] > 0 && Serial.peek() == '\r') { 326 | printSet(String(pager->alias)); 327 | printAddDevice(3, pager); 328 | return; 329 | } 330 | 331 | String alias = Serial.readString(); 332 | alias.replace("\n", ""); 333 | alias.replace("\r", ""); 334 | alias.trim(); 335 | alias.toCharArray(pager->alias, 10); 336 | printAddDevice(3, pager); 337 | return; 338 | } 339 | 340 | case 3: { 341 | reserBuf(); 342 | if (pager->frequency > 0) { 343 | arg = getCurStr() + pager->frequency; 344 | } 345 | 346 | Serial.print(F("Enter frequency, kHz")); 347 | Serial.println(arg); 348 | 349 | while (Serial.available() == 0) {} 350 | 351 | if (pager->frequency > 0 && Serial.peek() == '\r') { 352 | printSet(String(pager->frequency)); 353 | printAddDevice(4, pager); 354 | return; 355 | } 356 | 357 | srsL = Serial.readString().toInt(); 358 | 359 | if (srsL < 100000 | srsL > 500000) { 360 | Serial.println(F("Incorrect data")); 361 | printAddDevice(3, pager); 362 | } else { 363 | pager->frequency = srsL; 364 | printSet(String(srsL )); 365 | printAddDevice(4, pager); 366 | } 367 | 368 | return; 369 | } 370 | 371 | case 4: { 372 | reserBuf(); 373 | 374 | arg = getCurStr() + pager->msgSource; 375 | Serial.print(F("Enter source (0-3) or empty by default")); 376 | Serial.println(arg); 377 | 378 | while (Serial.available() == 0) { 379 | } 380 | 381 | if (Serial.peek() == '\r') { 382 | printSet(String(pager->msgSource)); 383 | printAddDevice(5, pager); 384 | return; 385 | } 386 | 387 | srs = Serial.parseInt(); 388 | 389 | if (srs >= 0 & srs <= 3) { 390 | pager->msgSource = srs; 391 | } 392 | 393 | printSet(String( pager->msgSource)); 394 | printAddDevice(5, pager); 395 | 396 | return; 397 | } 398 | 399 | case 5: { 400 | reserBuf(); 401 | arg = getCurStr() + pager->enconding; 402 | Serial.print(F("Enter enconding (0-EN Moto 1-EN/RU Moto 2-EN/RU Philips 3-RU Motorola)\r or empty by default")); 403 | Serial.println(arg); 404 | 405 | 406 | while (Serial.available() == 0) { 407 | } 408 | 409 | if (Serial.peek() == '\r') { 410 | printSet(String(pager->enconding)); 411 | printAddDevice(6, pager); 412 | return; 413 | } 414 | 415 | srs = Serial.parseInt(); 416 | if (srs > 0 & srs <= 3) { 417 | pager->enconding = srs; 418 | } 419 | printSet(String( pager->enconding)); 420 | printAddDevice(6, pager); 421 | 422 | return; 423 | } 424 | 425 | case 6: { 426 | reserBuf(); 427 | 428 | Serial.println(F("Enable inversion? Enter y/n or empty by default (n)")); 429 | 430 | while (Serial.available() == 0) { 431 | } 432 | 433 | if (Serial.peek() == '\r') { 434 | printSet(String(pager->inversion)); 435 | printAddDevice(7, pager); 436 | return; 437 | } 438 | 439 | 440 | char s = Serial.readString().charAt(0); 441 | if (s == 'y') { 442 | pager->inversion = 1; 443 | } else { 444 | pager->inversion = 0; 445 | } 446 | printSet(String( pager->inversion == 1 ? "Yes" : "No")); 447 | printAddDevice(7, pager); 448 | 449 | return; 450 | } 451 | 452 | case 7: { 453 | reserBuf(); 454 | arg = getCurStr() + pager->rate; 455 | Serial.print(F("Enter rate (512, 1200, 2400) or empty by default")); 456 | Serial.println(arg); 457 | 458 | while (Serial.available() == 0) { 459 | } 460 | 461 | if (Serial.peek() == '\r') { 462 | printSet(String(pager->rate)); 463 | printAddDevice(8, pager); 464 | return; 465 | } 466 | 467 | srs = Serial.parseInt(); 468 | 469 | if (srs == 512 | srs == 1200 | srs == 2400) { 470 | pager->rate = srs; 471 | } 472 | 473 | printSet(String(pager->rate)); 474 | printAddDevice(8, pager); 475 | return; 476 | } 477 | 478 | case 8: { 479 | reserBuf(); 480 | printPager(pager, true, true); 481 | Serial.println(F("\n\n [1] - Save, [2] - Cancel")); 482 | while (Serial.available() == 0) { 483 | } 484 | srs = Serial.parseInt(); 485 | switch (srs) { 486 | case 1: { 487 | addPager(pager, false); 488 | // memset(&pager, 0, sizeof(Pager)); 489 | Serial.println(F("Saved!\r\r\r")); 490 | printListMode(); 491 | reserBuf(); 492 | pauseCmd(0); 493 | break; 494 | } 495 | case 2: { 496 | reserBuf(); 497 | pauseCmd(0); 498 | printListMode(); 499 | break; 500 | } 501 | default: { 502 | reserBuf(); 503 | Serial.println(F("Sorry?")); 504 | printAddDevice(8, pager); 505 | break; 506 | } 507 | } 508 | 509 | } 510 | } 511 | } 512 | 513 | 514 | 515 | 516 | 517 | void printSending() { 518 | Serial.println(F("Sending...")); 519 | } 520 | 521 | void printSent() { 522 | Serial.println(F("Message sent")); 523 | } 524 | 525 | void printTrError() { 526 | Serial.println(F("Transmit error =/")); 527 | } 528 | -------------------------------------------------------------------------------- /UneversalPagerSender-arduino/UneversalPagerSender.ino: -------------------------------------------------------------------------------- 1 | /* 2 | POCSAG Universal Transmitter for Arduino Nano 3 | 4 | Extended by Hot Pixel 2022 5 | 6 | Based on https://github.com/SinuXVR/arduino-pocsag-transcoder 7 | Copyright (c) 2021, SinuX. All rights reserved. 8 | 9 | This library is distributed "as is" without any warranty. 10 | See MIT License for more details. 11 | */ 12 | #include "Rf7021.h" 13 | #include "PocsagEncoder.h" 14 | #include "CmdProc.h" 15 | #include "Cmd.h" 16 | #include "Storage.h" 17 | #include 18 | #include "MemoryFree.h" 19 | 20 | 21 | // RF7021 Wiring 22 | #define RF_CE_PIN 6 23 | #define RF_SLE_PIN 7 24 | #define RF_SDATA_PIN 8 25 | #define RF_SREAD_PIN 9 26 | #define RF_SCLK_PIN 10 27 | #define RF_TX_CLK_PIN 11 28 | #define RF_TX_DATA_PIN 12 29 | 30 | 31 | // RF7021 settings 32 | //#define RF_TCXO 12287154UL // 12.288 MHz TCXO 33 | #define RF_TCXO 12288114UL // 12.288 MHz TCXO 34 | #define RF_PRINT_ADF_INFO // Read and print chip revision, temperature and voltage at startup 35 | #define RF_TEST_MODE 4 // Test mode - "010101..." pattern 36 | #define RF_TEST_DURATION 10000 // Test duration 10 seconds 37 | 38 | // Pocsag default settings 39 | #define PCSG_DEFAULT_DATA_RATE 1200 // 1200bps 40 | #define PCSG_DEFAULT_FREQ 160000000UL // 160.000 MHz 41 | #define PCSG_DEFAULT_FREQ_DEV 4500 // 4.5 KHz 42 | #define PCSG_DEFAULT_CAP_CODE 1234567 // 7th frame 43 | #define PCSG_DEFAULT_SOURCE 3 // Alphanumeric 44 | #define PCSG_DEFAULT_ENCODING 0 // Latin (Motorola) 45 | 46 | static byte fixedIDs[STORAGE_COUNT]; 47 | 48 | Rf7021 rf = Rf7021(); 49 | PocsagEncoder pocsagEncoder = PocsagEncoder(); 50 | 51 | 52 | void resetFix() { 53 | for (int i = 0; i < STORAGE_COUNT; i++) { 54 | fixedIDs[i] = 0xFF; 55 | } 56 | } 57 | void resetDefaults() { 58 | rf.setDataRate(PCSG_DEFAULT_DATA_RATE); 59 | rf.setFrequency(PCSG_DEFAULT_FREQ); 60 | // Apply default encoder params 61 | pocsagEncoder.setCapCode(PCSG_DEFAULT_CAP_CODE); 62 | pocsagEncoder.setSource(PCSG_DEFAULT_SOURCE); 63 | pocsagEncoder.setEncodingId(PCSG_DEFAULT_ENCODING); 64 | } 65 | 66 | void setup() { 67 | 68 | // Set transmitter control pins 69 | rf.setCEPin(RF_CE_PIN); 70 | rf.setSLEPin(RF_SLE_PIN); 71 | rf.setSDATAPin(RF_SDATA_PIN); 72 | rf.setSREADPin(RF_SREAD_PIN); 73 | rf.setSCLKPin(RF_SCLK_PIN); 74 | rf.setTxRxCLKPin(RF_TX_CLK_PIN); 75 | rf.setTxRxDataPin(RF_TX_DATA_PIN); 76 | 77 | 78 | // Reset fixed IDs 79 | resetFix(); 80 | 81 | 82 | // Apply default frequency and deviation 83 | rf.setXtalFrequency(RF_TCXO); 84 | rf.setHasExternalInductor(1); 85 | 86 | rf.setFrequencyDeviation(PCSG_DEFAULT_FREQ_DEV); 87 | rf.setPowerAmplifierEnabled(1); 88 | resetDefaults(); 89 | 90 | 91 | cmdInit(9600); 92 | 93 | setWelcome(); 94 | printFree(); 95 | } 96 | 97 | void printFree() { 98 | // Serial.print(F("Free RAM = ")); 99 | // Serial.println(freeMemory()); 100 | } 101 | 102 | 103 | void transmit(String& msg, const Pager& pager, byte mode, byte raw) { 104 | 105 | rf.setFrequency(pager.frequency * 1000); 106 | rf.setDataInvertEnabled(pager.inversion); 107 | if (pager.rate != 1200 | pager.rate != 500 | pager.rate != 2400) 108 | rf.setDataRate(1200); 109 | else 110 | rf.setDataRate(pager.rate); 111 | pocsagEncoder.setCapCode(pager.cap); 112 | pocsagEncoder.setSource(pager.msgSource); 113 | pocsagEncoder.setEncodingId(pager.enconding); 114 | 115 | PocsagEncoder::PocsagMessage message; 116 | if (mode == 0) 117 | message = pocsagEncoder.encodeAlphanumeric(msg); 118 | else if (mode == 1) 119 | message = pocsagEncoder.encodeNumeric(msg); 120 | else if (mode == 2) { 121 | Serial.println(F("Dsnt work. Guru meditation...")); 122 | // message = pocsagEncoder.encodeTone(); 123 | } 124 | 125 | 126 | Serial.print(String(F("Sending to "))); 127 | if (raw) 128 | Serial.print(pager.cap); 129 | else 130 | Serial.print(pager.alias); 131 | Serial.print(F("... ")); 132 | 133 | if (rf.sendMessage(message.dataBytes, message.messageLength)) 134 | printSent(); 135 | else 136 | printTrError(); 137 | } 138 | 139 | /** 140 | Отправка команды в ручном режиме 141 | */ 142 | void sendCommands(int arg_cnt, char **args) { 143 | printFree(); 144 | Pager pager; 145 | for (int i = 0; i < arg_cnt; i++) { 146 | String arg = String(args[i]); 147 | 148 | // Deviation 149 | if (arg == "d") { 150 | if (arg_cnt > i + 1) { 151 | uint32_t freqDev = atoi(args[i + 1]); 152 | if (freqDev == 0 | freqDev > 10000) { 153 | errorIn(F("d")); 154 | return; 155 | } 156 | rf.setFrequencyDeviation(freqDev); 157 | Serial.print(F("Frequency Deviation: ")); 158 | Serial.print(freqDev / 1000.0, 2); 159 | Serial.println(F(" KHz")); 160 | } else { 161 | errorIn(F("d")); 162 | } 163 | } 164 | 165 | 166 | // Freq 167 | if (arg == "f") { 168 | if (arg_cnt > i + 1) { 169 | uint32_t freq = atol(args[i + 1]); 170 | if (freq >= 80000 && freq <= 950000) { 171 | pager.frequency = freq; 172 | rf.setFrequency(freq * 1000); 173 | Serial.print(F("Frequency: ")); 174 | Serial.print(freq / 1000.0, 3); 175 | Serial.println(F(" MHz")); 176 | } else { 177 | Serial.println(freq); 178 | Serial.println(F("Wrong frequency, should be 80000 to 950000 kHz")); 179 | } 180 | } else { 181 | errorIn(F("f")); 182 | } 183 | } 184 | 185 | //Inversion 186 | if (arg == "i") { 187 | if (arg_cnt > i + 1) { 188 | byte inv = atoi(args[i + 1]); 189 | if (inv > 1) { 190 | errorIn(F("i")); 191 | return; 192 | } else { 193 | pager.inversion = inv; 194 | Serial.print(F("Inversion: ")); 195 | Serial.println(inv ? F("enabled") : F("disabled")); 196 | } 197 | } else { 198 | errorIn(F("i")); 199 | } 200 | } 201 | 202 | // Baut 203 | if (arg == "r") { 204 | if (arg_cnt > i + 1) { 205 | word rate = atoi(args[i + 1]); 206 | if (rate == 512 || rate == 1200 || rate == 2400) { 207 | pager.rate = rate; 208 | Serial.print(F("Data rate: ")); 209 | Serial.println(rate); 210 | } else { 211 | rf.setDataRate(rate); 212 | Serial.println(F("Wrong data rate, should be 512, 1200 or 2400")); 213 | } 214 | } else { 215 | errorIn(F("r")); 216 | } 217 | } 218 | 219 | // Test 220 | if (arg == "x") { 221 | Serial.println(F("Testing...")); 222 | rf.setFrequency(pager.frequency); 223 | rf.txTest(1, 15000); 224 | Serial.println(F("Test completed")); 225 | } 226 | 227 | // Тонкая подстройка частоты кварца. 228 | // Позволяет компенсировать погрешность кварца и "подтянуть" частоту передачи 229 | // к эталонной. После этой команды передается тестовый сигнал, который поможет 230 | // определить степень подстройки. После подстройки необходимо вписать полученное 231 | // значение в константу RF_TCXO 232 | 233 | // Увеличить частоту, Гц. 234 | if (arg == "h") { 235 | if (arg_cnt > i + 1) { 236 | int argUp = atoi(args[i + 1]); 237 | rf.setXtalFrequency(rf.getXtalFrequency() + argUp); 238 | Serial.println(rf.getXtalFrequency()); 239 | rf.txTest(1, 3000); 240 | } else { 241 | errorIn(F("u")); 242 | } 243 | } 244 | 245 | // Понизить частоту, Гц 246 | if (arg == "l") { 247 | if (arg_cnt > i + 1) { 248 | int argUp = atoi(args[i + 1]); 249 | rf.setXtalFrequency(rf.getXtalFrequency() - argUp); 250 | Serial.println(rf.getXtalFrequency()); 251 | rf.txTest(1, 3000); 252 | } else { 253 | errorIn(F("j")); 254 | } 255 | } 256 | 257 | // CAP 258 | if (arg == "c") { 259 | if (arg_cnt > i + 1) { 260 | uint32_t cap = atol(args[i + 1]); 261 | if (cap <= 9999999) { 262 | pager.cap = cap; 263 | Serial.print(F("CAP code: ")); 264 | Serial.println(cap); 265 | } else { 266 | Serial.println(F("Wrong CAP")); 267 | 268 | return; 269 | } 270 | } else { 271 | errorIn(F("c")); 272 | } 273 | } 274 | 275 | // Source code 276 | if (arg == "s") { 277 | if (arg_cnt > i + 1) { 278 | byte src = atoi(args[i + 1]); 279 | if (src <= 3) { 280 | pager.msgSource = src; 281 | Serial.print(F("Source code: ")); 282 | Serial.println(src); 283 | } else { 284 | Serial.println(F("Wrong source code, should be 0, 1, 2 or 3")); 285 | 286 | return; 287 | } 288 | } else { 289 | errorIn(F("s")); 290 | } 291 | } 292 | 293 | 294 | // Encoding 295 | if (arg == "e") { 296 | if (arg_cnt > i + 1) { 297 | byte enc = atoi(args[i + 1]); 298 | if (enc <= 3) { 299 | pager.enconding = enc; 300 | Serial.print(F("Encoding: ")); 301 | Serial.print(enc); 302 | Serial.println(enc == 0 ? F(" (EN Motorola)") : enc == 1 ? F(" (EN/RU Motorola)") : enc == 2 ? F(" (EN/RU Philips)") : enc == 3 ? F(" (RU Motorola)") : F(" (Unknown)")); 303 | } else { 304 | Serial.println(F("Wrong encoding, should be 0, 1, 2 or 3")); 305 | 306 | return; 307 | } 308 | } else { 309 | errorIn(F("e")); 310 | } 311 | } 312 | 313 | // Send numeric message 314 | if (arg == "n") { 315 | if (arg_cnt > i + 1) { 316 | String msg = args[i + 1]; 317 | transmit(msg, pager, 1, 1); 318 | delay(30); 319 | } else { 320 | errorIn(F("n")); 321 | } 322 | } 323 | 324 | // Send text message 325 | if (arg == "m") { 326 | printFree(); 327 | if (arg_cnt > i + 1) { 328 | 329 | String msg = ""; 330 | for (int ii = i + 1; ii < arg_cnt; ii++) { 331 | msg = msg + String(args[ii]) + " "; 332 | } 333 | 334 | transmit(msg, pager, 0, 1); 335 | delay(30); 336 | printFree(); 337 | } else { 338 | Serial.println(F("Message empty")); 339 | } 340 | break; 341 | } 342 | 343 | 344 | // Send tone message 345 | if (arg == "t") { 346 | // PocsagEncoder::PocsagMessage message = pocsagEncoder.encodeTone(); 347 | // printSending(); 348 | // rf.sendMessage(message.dataBytes, message.messageLength); 349 | 350 | String msg = ""; 351 | transmit(msg, pager, 2, 1); 352 | printSent(); 353 | } 354 | } 355 | } 356 | 357 | /** 358 | Фиксация нужных пейджеров (для отпавки одного сообщения на несколько устройств) 359 | */ 360 | void addFixedIDs(int arg_cnt, char **args) { 361 | if (arg_cnt == 1) { 362 | Serial.println(F("No IDs..")); 363 | return; 364 | } 365 | resetFix(); 366 | for (byte i = 1; i < arg_cnt; i++) { 367 | fixedIDs[i - 1] = atoi( args[i]); 368 | } 369 | printFixAdded(fixedIDs); 370 | } 371 | 372 | 373 | /** 374 | Отправка на зафиксированные пейджеры 375 | */ 376 | void sendToFixed(int arg_cnt, char **args) { 377 | 378 | if (arg_cnt == 1) { 379 | Serial.println(F("Message not found")); 380 | return; 381 | } 382 | 383 | if (fixedIDs[0] == 0xff) { 384 | Serial.println(F("Set IDs first")); 385 | return; 386 | } 387 | 388 | String msg = ""; 389 | for (byte i = 1; i < arg_cnt; i++) { 390 | for (int ii = i + 1; ii < arg_cnt; ii++) { 391 | msg += String(args[ii]) + " "; 392 | } 393 | } 394 | 395 | for (int i = 0; i < STORAGE_COUNT ; i++) { 396 | if (fixedIDs[i] == 0xff) 397 | continue; 398 | 399 | Pager pager; 400 | getPager(fixedIDs[i], &pager); 401 | 402 | // if detect pager struct 403 | if (pager.crc[0] == CRC[0] && pager.crc[1] == CRC[1]) { 404 | transmit(msg, pager, 0, 0); 405 | } else { 406 | Serial.println(String(F("ID not found: ")) + args[i]); 407 | } 408 | delay(30); 409 | } 410 | printFree(); 411 | } 412 | 413 | void sendSpecCmdTo(int arg_cnt, char **args) { 414 | String msg = ""; 415 | int indexMsg = -1; 416 | byte mode = 0; // 0 - text, 1 - numeric, 2 - tone 417 | 418 | // Ищем тело сообщения 419 | for (byte i = 0; i < arg_cnt; i++) { 420 | if ((strcmp(args[i], "send") == 0 | strcmp(args[i], "numeric") == 0) && arg_cnt > i + 1) { 421 | indexMsg = i; 422 | if (strcmp(args[i], "send") == 0) 423 | mode = 0; 424 | else 425 | mode = 1; 426 | for (int ii = i + 1; ii < arg_cnt; ii++) { 427 | msg = msg + String(args[ii]) + " "; 428 | } 429 | break; 430 | } 431 | // if Tone mode 432 | if (strcmp(args[i], "tone") == 0) { 433 | indexMsg = i; 434 | mode = 2; 435 | } 436 | } 437 | 438 | if (mode != 2 && indexMsg < 0) { 439 | errorIn(F("Message not found")); 440 | return; 441 | } 442 | 443 | // Ищем IDы 444 | if (indexMsg < 2) { 445 | errorIn(F("IDs not found")); 446 | return; 447 | } 448 | printFree(); 449 | 450 | for (int i = 1; i < indexMsg; i++) { 451 | Pager pager; 452 | getPager(atoi(args[i]), &pager); 453 | 454 | 455 | if (pager.crc[0] == CRC[0] && pager.crc[1] == CRC[1]) { 456 | transmit(msg, pager, mode, 0); 457 | delay(30); 458 | } else { 459 | Serial.println(String(F("ID not found: ")) + args[1]); 460 | } 461 | printFree(); 462 | } 463 | } 464 | 465 | 466 | /** 467 | Режим приветствия 468 | */ 469 | void setWelcome() { 470 | printFree(); 471 | printWelcome(rf); 472 | clearCmd(); 473 | cmdAdd("1", setFreeMode); 474 | cmdAdd("2", setSpecMode); 475 | cmdAdd("3", setSavedPagers); 476 | cmdAdd("0", setWelcome); 477 | cmdAdd("send", sendCommands); 478 | cmdAdd("testfreq", startTestFreq); 479 | cmdAdd("testrate", startRateTest); 480 | printFree(); 481 | } 482 | 483 | /** 484 | Режим отправки на сохраненные устройства 485 | */ 486 | void setSpecMode() { 487 | printSpecMode(); 488 | clearCmd(); 489 | cmdAdd("to", sendSpecCmdTo); 490 | cmdAdd("fix", addFixedIDs); 491 | cmdAdd("send", sendToFixed); 492 | cmdAdd("0", setWelcome); 493 | printFree(); 494 | } 495 | 496 | 497 | void addDevice() { 498 | struct Pager pager; 499 | pager.addr = -1; 500 | printAddDevice(0, &pager); 501 | } 502 | 503 | void removeDevice(int arg_cnt, char **args) { 504 | if (arg_cnt == 1) { 505 | return; 506 | } 507 | struct Pager pager; 508 | getPager(atoi(args[1]), &pager); 509 | addPager(&pager, true); 510 | setSavedPagers() ; 511 | } 512 | 513 | 514 | void editDevice(int arg_cnt, char **args) { 515 | if (arg_cnt == 0) { 516 | return; 517 | } 518 | pauseCmd(1); 519 | struct Pager pager ; 520 | getPager(String(args[1]).toInt(), &pager); 521 | printAddDevice(0, &pager); 522 | } 523 | 524 | void setSavedPagers() { 525 | printListMode(); 526 | clearCmd(); 527 | cmdAdd("add", addDevice); 528 | cmdAdd("del", removeDevice); 529 | cmdAdd("edit", editDevice); 530 | cmdAdd("0", setWelcome ); 531 | printFree(); 532 | } 533 | 534 | 535 | void setFreeMode() { 536 | printFreeMode(); 537 | clearCmd(); 538 | cmdAdd("full", printFreeModeAdv); 539 | cmdAdd("0", setWelcome); 540 | cmdAdd("send", sendCommands); 541 | printFree(); 542 | } 543 | 544 | /** 545 | Тест для поиска точной частоты приемника 546 | Базовой установленной частоте дается гистерезис 20 кГц 547 | и производится цикличная отправка с текстом текущей частоты 548 | */ 549 | void startTestFreq() { 550 | long fr = rf.getFrequency() - 20000; 551 | for (int i = 0; i < 40; i = i + 1) { 552 | fr += 1000; 553 | rf.setFrequency(fr); 554 | PocsagEncoder::PocsagMessage message = pocsagEncoder.encodeAlphanumeric(String(fr / 1000)); 555 | Serial.println(String("sending ") + (fr / 1000)); 556 | rf.sendMessage(message.dataBytes, message.messageLength); 557 | delay(20); 558 | } 559 | Serial.println(String("Complete.")); 560 | } 561 | 562 | /** 563 | Тест для поиска рабочей скорости передачи. 564 | Если используется кварц без термостабилизации (не TCXO), 565 | возможен фактический уход таймингов от установленных 566 | */ 567 | void startRateTest() { 568 | word rate = 1190; 569 | for (int i = 0; i < 20; i = i + 1) { 570 | rate = rate + 1; 571 | rf.setDataRate(rate); 572 | PocsagEncoder::PocsagMessage message = pocsagEncoder.encodeAlphanumeric(String(rate)); 573 | Serial.println(String("sending ") + (rate)); 574 | rf.sendMessage(message.dataBytes, message.messageLength); 575 | delay(130); 576 | } 577 | Serial.println(String("sending ")); 578 | } 579 | 580 | 581 | void unrecognized() 582 | { 583 | Serial.println(F("Sorry?")); 584 | } 585 | 586 | void errorIn(const & msg) { 587 | Serial.print(F("Error in ") ); 588 | Serial.println(msg); 589 | } 590 | 591 | 592 | void loop() { 593 | cmdPoll(); 594 | } 595 | --------------------------------------------------------------------------------