├── .gitattributes ├── ESP32-firmware ├── include │ ├── led.h │ ├── lcd.h │ ├── httpd.h │ ├── jwifi.h │ ├── bitutils.h │ ├── decoder.h │ ├── pagerkeepalive.h │ ├── encoder.h │ ├── BCH3121.h │ ├── statustracker.h │ ├── README │ ├── pocsagutils.h │ ├── settings.h │ └── flexutils.h ├── replace_fs.py ├── mklittlefs.exe ├── otherstuff │ ├── bg.bmp │ ├── bg.png │ ├── idle.png │ ├── lcars16.vlw │ ├── lcars24.vlw │ ├── message.png │ ├── synced.png │ └── nosignal.png ├── data │ └── www │ │ ├── favicon.ico │ │ ├── roll.svg │ │ ├── index.html │ │ ├── main.js │ │ ├── main.css │ │ └── p2000.svg ├── .gitignore ├── .vscode │ ├── settings.json │ └── extensions.json ├── test │ └── README ├── lib │ └── README ├── platformio.ini └── src │ ├── main.cpp │ ├── bitutils.cpp │ ├── led.cpp │ ├── ota.cpp │ ├── BCH3121.cpp │ ├── pagerkeepalive.cpp │ ├── jwifi.cpp │ ├── statustracker.cpp │ ├── lcd.cpp │ ├── settings.cpp │ ├── httpd.cpp │ └── pocsagutils.cpp ├── POCSAG-Modem Eagle Files ├── main_00.job ├── main_01.job ├── main_02.job ├── main_03.job ├── main_04.job ├── main-single-esp32_2020-10-23.zip ├── GerberFiles │ ├── silkscreen_bottom.gbo │ ├── profile.gko │ ├── solderpaste_bottom.gbr │ ├── gerber_job.gbrjob │ ├── vias.gbr │ ├── solderpaste_top.gbr │ ├── soldermask_bottom.gbs │ └── soldermask_top.gts ├── CAMOutputs │ ├── Assembly │ │ ├── PnP_main-single-esp32_back.txt │ │ ├── PnP_main-single-esp32_front.txt │ │ └── main-single-esp32.txt │ ├── GerberFiles │ │ ├── profile.gbr │ │ ├── solderpaste_bottom.gbr │ │ ├── gerber_job.gbrjob │ │ └── solderpaste_top.gbr │ └── DrillFiles │ │ └── drill_1_16.xln ├── main_01.pro ├── main_02.pro ├── main_03.pro ├── main_04.pro ├── main-single-esp32.pro ├── main_00.pro ├── DrillFiles │ └── drills.xln └── eagle.epf ├── README.md └── WDS - radio-config ├── genconfig.php └── pocsag_2400_tx.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /ESP32-firmware/include/led.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void ledTask(void *parameter); 4 | -------------------------------------------------------------------------------- /ESP32-firmware/include/lcd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void lcdTask(void *parameter); 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ESP32-firmware/replace_fs.py: -------------------------------------------------------------------------------- 1 | Import("env") 2 | env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/mklittlefs' ) -------------------------------------------------------------------------------- /ESP32-firmware/mklittlefs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/mklittlefs.exe -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/bg.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/bg.bmp -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/bg.png -------------------------------------------------------------------------------- /ESP32-firmware/data/www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/data/www/favicon.ico -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/idle.png -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/lcars16.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/lcars16.vlw -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/lcars24.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/lcars24.vlw -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/message.png -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/synced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/synced.png -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_00.job: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main_00.job -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_01.job: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main_01.job -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_02.job: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main_02.job -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_03.job: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main_03.job -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_04.job: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main_04.job -------------------------------------------------------------------------------- /ESP32-firmware/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /ESP32-firmware/otherstuff/nosignal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/ESP32-firmware/otherstuff/nosignal.png -------------------------------------------------------------------------------- /ESP32-firmware/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}" 3 | } -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main-single-esp32_2020-10-23.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjwbruijn/POCSAG_FLEX_Modem/HEAD/POCSAG-Modem Eagle Files/main-single-esp32_2020-10-23.zip -------------------------------------------------------------------------------- /ESP32-firmware/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/silkscreen_bottom.gbo: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSilkscreen Bottom*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | 12 | 13 | M02* 14 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/Assembly/PnP_main-single-esp32_back.txt: -------------------------------------------------------------------------------- 1 | C1 36.20 57.79 0.00 1µF C1206K 2 | C2 10.79 75.56 180.00 1µF C1206K 3 | C3 10.79 73.03 180.00 10µF C1206K 4 | C4 36.20 60.33 0.00 10µF C1206K 5 | C7 33.02 15.88 90.00 1µF C1206K 6 | C8 36.20 75.56 0.00 1µF C1206K 7 | -------------------------------------------------------------------------------- /ESP32-firmware/include/httpd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern bool HTTPrestartNeeded; 4 | extern void httpdProcess(void *parameter); 5 | extern void initHTTPD(void); 6 | 7 | #define WS_SEND_MODE_STATUS 0x01 8 | #define WS_SEND_SIGNAL 0x02 9 | #define WS_SEND_MODMODE 0x04 10 | #define WS_SEND_FRAME_STATUS 0x08 -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/profile.gko: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %IN*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10C,0.254000*% 12 | 13 | 14 | D10* 15 | X0Y0D02* 16 | X540000Y0D01* 17 | X540000Y800000D01* 18 | X0Y800000D01* 19 | X0Y0D01* 20 | M02* 21 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/GerberFiles/profile.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %IN*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10C,0.254000*% 12 | 13 | 14 | D10* 15 | X0Y0D02* 16 | X540000Y0D01* 17 | X540000Y800000D01* 18 | X0Y800000D01* 19 | X0Y0D01* 20 | M02* 21 | -------------------------------------------------------------------------------- /ESP32-firmware/include/jwifi.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern char macStr[13]; 4 | extern char shortmac[6]; 5 | extern volatile bool wifiConnected; 6 | enum wifistatusenum { 7 | READY, 8 | DISCONNECTED, 9 | GOT_IP, 10 | CONNECTED 11 | }; 12 | 13 | extern void wifiReset(void); 14 | extern void wifiProcess(void* parameter); 15 | extern void wifiResetConfig(void); 16 | extern void wifiSaveConfigCallback(void); 17 | extern void wifiInit(void); -------------------------------------------------------------------------------- /ESP32-firmware/include/bitutils.h: -------------------------------------------------------------------------------- 1 | #include 2 | char IRAM_ATTR bitswitch(char b); 3 | uint32_t IRAM_ATTR createcrc(uint32_t in); 4 | uint8_t IRAM_ATTR swap(uint8_t b); 5 | uint16_t IRAM_ATTR swap16(uint16_t b); 6 | uint32_t swap32(uint32_t n); 7 | uint32_t addValue(uint8_t offset, uint8_t bsize, uint32_t value); 8 | uint32_t getValue(uint8_t offset, uint8_t bsize, uint32_t value); 9 | uint32_t motchecksum(uint32_t in); 10 | bool validateMotChecksum(uint32_t word); 11 | uint8_t IRAM_ATTR countbits(uint32_t val); -------------------------------------------------------------------------------- /ESP32-firmware/test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /ESP32-firmware/include/decoder.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define RX_WAIT_SYNC 0 6 | #define RX_WAIT_ADDR 1 7 | #define RX_IN_MESSAGE 2 8 | 9 | extern SemaphoreHandle_t wsMutex; 10 | extern volatile bool resetRX; 11 | void timerInterrupt(void); 12 | void pocsagReadBit(uint8_t val); 13 | void IRAM_ATTR pocsagRXInterrupt(); 14 | void pocsagProcessing(void* parameter); 15 | void IRAM_ATTR flexsynchelper(); 16 | char numericDecode(uint8_t c); 17 | void pocsagTask(void* parameter); 18 | void flexProcessing(void* parameter); 19 | void flexTask(void *parameter); 20 | void flexTest(); -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/solderpaste_bottom.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSolderpaste Bottom*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,1.500000X2.000000*% 12 | %ADD11R,2.000000X1.500000*% 13 | 14 | 15 | D10* 16 | X376950Y577850D03* 17 | X346950Y577850D03* 18 | X92950Y755650D03* 19 | X122950Y755650D03* 20 | X92950Y730250D03* 21 | X122950Y730250D03* 22 | X376950Y603250D03* 23 | X346950Y603250D03* 24 | D11* 25 | X330200Y143750D03* 26 | X330200Y173750D03* 27 | D10* 28 | X376950Y755650D03* 29 | X346950Y755650D03* 30 | M02* 31 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/GerberFiles/solderpaste_bottom.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSolderpaste Bottom*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,1.500000X2.000000*% 12 | %ADD11R,2.000000X1.500000*% 13 | 14 | 15 | D10* 16 | X376950Y577850D03* 17 | X346950Y577850D03* 18 | X92950Y755650D03* 19 | X122950Y755650D03* 20 | X92950Y730250D03* 21 | X122950Y730250D03* 22 | X376950Y603250D03* 23 | X346950Y603250D03* 24 | D11* 25 | X330200Y143750D03* 26 | X330200Y173750D03* 27 | D10* 28 | X376950Y755650D03* 29 | X346950Y755650D03* 30 | M02* 31 | -------------------------------------------------------------------------------- /ESP32-firmware/data/www/roll.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/gerber_job.gbrjob: -------------------------------------------------------------------------------- 1 | { 2 | "Header": { 3 | "Comment": "All values are metric (mm)", 4 | "CreationDate": "2020-10-23T10:14:48Z", 5 | "GenerationSoftware": { 6 | "Application": "EAGLE", 7 | "Vendor": "Autodesk", 8 | "Version": "9.6.2" 9 | }, 10 | "Part": "Single" 11 | }, 12 | "Overall": { 13 | "BoardThickness": 1.67, 14 | "LayerNumber": 2, 15 | "Name": { 16 | "ProjectId": "main-single-esp32" 17 | }, 18 | "Owner": "Jelmer Bruijn ", 19 | "Size": { 20 | "X": 54, 21 | "Y": 80 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/GerberFiles/gerber_job.gbrjob: -------------------------------------------------------------------------------- 1 | { 2 | "Header": { 3 | "Comment": "All values are metric (mm)", 4 | "CreationDate": "2020-10-23T10:40:54Z", 5 | "GenerationSoftware": { 6 | "Application": "EAGLE", 7 | "Vendor": "Autodesk", 8 | "Version": "9.6.2" 9 | }, 10 | "Part": "Single" 11 | }, 12 | "Overall": { 13 | "BoardThickness": 1.67, 14 | "LayerNumber": 2, 15 | "Name": { 16 | "ProjectId": "main-single-esp32" 17 | }, 18 | "Owner": "Jelmer Bruijn ", 19 | "Size": { 20 | "X": 54, 21 | "Y": 80 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ESP32-firmware/include/pagerkeepalive.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ESPAsyncWebServer.h" 3 | #include 4 | 5 | struct idlelistentry { 6 | uint16_t frame; 7 | uint32_t freq; 8 | uint16_t timeout; 9 | }; 10 | //extern std::vector flexidlelist; 11 | 12 | extern bool registerFrame(uint32_t freq, uint32_t ric, char prefix); 13 | bool registerPOCSAG(uint32_t freq, uint16_t baud); 14 | void resetIdleCounter(uint32_t freq, uint16_t frame); 15 | void decrementIdleCounter(); 16 | uint32_t shouldSendIdleFlexFrame(uint8_t frame); 17 | idlelistentry shouldSendIdlePOCSAG(); 18 | void delFromIdleList(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); 19 | void getIdleList(AsyncWebServerRequest* request); 20 | void loadIdleList(); 21 | void saveIdleList(); 22 | -------------------------------------------------------------------------------- /ESP32-firmware/data/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | P2000 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |
    18 | 19 | 20 |
    21 |
    22 | 23 |
      24 |
    • Wachten op berichtgeving..
    • 25 |
    26 | 27 |
    28 |
    29 | 30 | 31 |
    32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/Assembly/PnP_main-single-esp32_front.txt: -------------------------------------------------------------------------------- 1 | C10 31.75 50.16 270.00 10µF C1206K 2 | C11 29.21 50.16 270.00 10µF C1206K 3 | C12 26.67 50.16 270.00 10µF C1206K 4 | C13 34.29 6.99 0.00 5v D/7343-31W 5 | C14 41.27 50.80 0.00 3v3 D/7343-31W 6 | C15 20.96 24.13 0.00 1µF C0805 7 | C16 20.96 16.51 0.00 1µF C0805 8 | C17 20.96 8.89 0.00 1µF C0805 9 | C18 22.23 34.93 270.00 1µF C0805 10 | C19 20.96 31.75 0.00 1µF C0805 11 | C5 22.23 40.01 90.00 10µF C1206K 12 | C6 10.79 42.55 270.00 10µF C1206K 13 | C9 22.23 47.63 90.00 12v D/7343-31W 14 | D1 6.99 15.88 0.00 SMA-DIODE 15 | D2 26.04 31.75 90.00 WS2812B 16 | D3 26.04 39.37 90.00 WS2812B 17 | D4 26.04 24.13 90.00 WS2812B 18 | D5 26.04 16.51 90.00 WS2812B 19 | D6 26.04 8.89 90.00 WS2812B 20 | D7 17.14 34.29 0.00 SMA-DIODE 21 | IC1 15.88 40.64 180.00 LD117AS33TR SOT223 22 | IC3 12.82 48.90 270.00 7805DT TO252 23 | R8 15.88 20.32 270.00 2k R1206 24 | R9 13.67 20.32 90.00 10k R1206 25 | T1 12.70 15.88 90.00 SOT23-BEC 26 | U$1 39.77 27.73 270.00 RF4463F30 NICERF_16PIN_SPI_TRANSCEIVER 27 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_01.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main.brd 4 | 5 | Start at : 23:39:18 (21/10/2020) 6 | End at : 23:39:21 (21/10/2020) 7 | Elapsed time : 00:00:01 8 | 9 | Signals : 32 RoutingGrid: 25 mil Layers: 2 10 | Connections : 94 predefined: 5 ( 0 Vias ) 11 | 12 | Router memory : 94952 13 | 14 | Passname : Busses Route Optimize1 Optimize2 Optimize3 Optimize4 15 | 16 | Time per pass : 00:00:00 00:00:00 00:00:01 00:00:00 00:00:00 00:00:00 17 | Number of Ripups : 0 1 0 0 0 0 18 | max. Level : 0 1 0 0 0 0 19 | max. Total : 0 14 0 0 0 0 20 | 21 | Routed : 8 77 77 77 77 77 22 | Vias : 0 109 52 38 35 36 23 | Resolution : 13.8 % 87.2 % 87.2 % 87.2 % 87.2 % 87.2 % 24 | 25 | Final : 26 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_02.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main.brd 4 | 5 | Start at : 23:39:18 (21/10/2020) 6 | End at : 23:39:22 (21/10/2020) 7 | Elapsed time : 00:00:02 8 | 9 | Signals : 32 RoutingGrid: 25 mil Layers: 2 10 | Connections : 94 predefined: 5 ( 0 Vias ) 11 | 12 | Router memory : 94952 13 | 14 | Passname : Busses Route Optimize1 Optimize2 Optimize3 Optimize4 15 | 16 | Time per pass : 00:00:00 00:00:00 00:00:01 00:00:00 00:00:00 00:00:01 17 | Number of Ripups : 0 0 0 0 0 0 18 | max. Level : 0 0 0 0 0 0 19 | max. Total : 0 0 0 0 0 0 20 | 21 | Routed : 15 77 77 77 77 77 22 | Vias : 0 93 49 43 43 41 23 | Resolution : 21.3 % 87.2 % 87.2 % 87.2 % 87.2 % 87.2 % 24 | 25 | Final : 26 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_03.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main.brd 4 | 5 | Start at : 23:39:18 (21/10/2020) 6 | End at : 23:39:25 (21/10/2020) 7 | Elapsed time : 00:00:05 8 | 9 | Signals : 32 RoutingGrid: 12.5 mil Layers: 2 10 | Connections : 94 predefined: 5 ( 0 Vias ) 11 | 12 | Router memory : 359176 13 | 14 | Passname : Busses Route Optimize1 Optimize2 Optimize3 Optimize4 15 | 16 | Time per pass : 00:00:00 00:00:01 00:00:01 00:00:01 00:00:01 00:00:01 17 | Number of Ripups : 0 0 0 0 0 0 18 | max. Level : 0 0 0 0 0 0 19 | max. Total : 0 0 0 0 0 0 20 | 21 | Routed : 10 89 89 89 89 89 22 | Vias : 0 119 56 41 40 39 23 | Resolution : 16.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 24 | 25 | Final : 26 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_04.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main.brd 4 | 5 | Start at : 23:39:18 (21/10/2020) 6 | End at : 23:39:25 (21/10/2020) 7 | Elapsed time : 00:00:05 8 | 9 | Signals : 32 RoutingGrid: 12.5 mil Layers: 2 10 | Connections : 94 predefined: 5 ( 0 Vias ) 11 | 12 | Router memory : 359176 13 | 14 | Passname : Busses Route Optimize1 Optimize2 Optimize3 Optimize4 15 | 16 | Time per pass : 00:00:00 00:00:01 00:00:01 00:00:01 00:00:01 00:00:01 17 | Number of Ripups : 0 0 0 0 0 0 18 | max. Level : 0 0 0 0 0 0 19 | max. Total : 0 0 0 0 0 0 20 | 21 | Routed : 17 89 89 89 89 89 22 | Vias : 0 95 52 47 45 48 23 | Resolution : 23.4 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 24 | 25 | Final : 26 | -------------------------------------------------------------------------------- /ESP32-firmware/include/encoder.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "flexutils.h" 6 | #include "pocsagutils.h" 7 | 8 | struct txframe { 9 | bool blocked; 10 | enum frameformat { FORMAT_FLEX, 11 | FORMAT_POCSAG, 12 | FORMAT_BLOCKED, 13 | FORMAT_IDLE } format; 14 | struct flexframe *flex = NULL; 15 | class pocsagdata *pocsag = NULL; 16 | ~txframe(); 17 | txframe(enum frameformat format); 18 | }; 19 | 20 | struct statusframe { 21 | uint8_t frameno; 22 | enum txframe::frameformat txformat; 23 | enum flexsynctype rxtype; 24 | bool isTX; 25 | bool txCancelled; 26 | uint32_t freq; 27 | }; 28 | 29 | #define STATUSFRAMELISTSIZE 10 30 | extern struct statusframe* statusframearr[]; 31 | 32 | void syncTask(void *parameter); 33 | void eachFrame(); 34 | void startFrameAlignedTX(bool channelClear, bool shortPreamble); 35 | void shiftStatusFrames(); 36 | int addPOCSAG(pocsagaddr* addr, uint32_t freq, uint16_t baudrate, char* data); 37 | int addFLEX(char pre, uint32_t ric, uint32_t freq, char *data, bool isNumeric); 38 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main-single-esp32.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main-single-esp32.brd 4 | 5 | Start at : 10:03:43 (23-10-2020) 6 | End at : 10:03:50 (23-10-2020) 7 | Elapsed time : 00:00:04 8 | 9 | Signals : 25 RoutingGrid: 17 mil Layers: 2 10 | Connections : 95 predefined: 85 ( 10 Vias ) 11 | 12 | Router memory : 108504 13 | 14 | Passname : TopRouter Route Optimize1 Optimize2 Optimize3 Optimize4 15 | 16 | Time per pass : 00:00:04 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 17 | Number of Ripups : 0 0 0 0 0 0 18 | max. Level : 0 0 0 0 0 0 19 | max. Total : 0 0 0 0 0 0 20 | 21 | Routed : 6 10 10 10 10 10 22 | Vias : 0 7 12 10 10 10 23 | Resolution : 95.8 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 24 | 25 | Final : 100.0% finished 26 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/main_00.pro: -------------------------------------------------------------------------------- 1 | EAGLE AutoRouter Statistics: 2 | 3 | Job : Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main.brd 4 | 5 | Start at : 23:39:18 (21/10/2020) 6 | End at : 23:39:42 (21/10/2020) 7 | Elapsed time : 00:00:22 8 | 9 | Signals : 32 RoutingGrid: 6 mil Layers: 2 10 | Connections : 94 predefined: 5 ( 0 Vias ) 11 | 12 | Router memory : 1518860 13 | 14 | Job has been interrupted! 15 | 16 | Passname : TopRouter Route Optimize1 Optimize2 Optimize3 Optimize4 17 | 18 | Time per pass : 00:00:03 00:00:05 00:00:05 00:00:04 00:00:04 00:00:01 19 | Number of Ripups : 0 1 0 0 0 0 20 | max. Level : 0 1 0 0 0 0 21 | max. Total : 0 11 0 0 0 0 22 | 23 | Routed : 23 89 89 89 89 89 24 | Vias : 0 42 49 45 45 45 25 | Resolution : 29.8 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 26 | 27 | Final : 28 | -------------------------------------------------------------------------------- /ESP32-firmware/lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /ESP32-firmware/include/BCH3121.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Copyright (C) 2018 by Andy Uribe CA6JAU 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #if !defined(BCH3121_H) 22 | #define BCH3121_H 23 | 24 | class CBCH3121 { 25 | public: 26 | CBCH3121(); 27 | 28 | void encode(uint32_t& data); 29 | bool decode(uint32_t& data, uint16_t& errors); 30 | 31 | private: 32 | void calc_syndrome(uint32_t data); 33 | bool calc_parity(uint32_t data); 34 | uint16_t check_parity(uint32_t& data); 35 | 36 | int8_t m_S1; 37 | int8_t m_S3; 38 | 39 | }; 40 | 41 | #endif -------------------------------------------------------------------------------- /ESP32-firmware/include/statustracker.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define STATUS_WIFI_ACTIVITY 0x01 4 | #define STATUS_RX_NO_SIGNAL 0x02 5 | #define STATUS_RX_SIGNAL 0x03 6 | #define STATUS_TX 0x04 7 | #define STATUS_IDLE 0x05 8 | #define STATUS_MODE_FLEX1600 0x06 9 | #define STATUS_MODE_FLEX3200_2 0x07 10 | #define STATUS_MODE_FLEX3200_4 0x08 11 | #define STATUS_MODE_FLEX6400_4 0x09 12 | #define STATUS_MODE_POCSAG512 0x0A 13 | #define STATUS_MODE_POCSAG1200 0x0B 14 | #define STATUS_MODE_POCSAG2400 0x0C 15 | #define STATUS_MODE_IDLE 0x0D 16 | #define STATUS_NEW_WS_CONNECTION 0x0E 17 | #define STATUS_SYNCED_FLEX 0x0F 18 | #define STATUS_SYNCED_GPS 0x10 19 | #define STATUS_SYNCED_NOSYNC 0x11 20 | #define STATUS_FRAMELIST_UPDATE 0x12 21 | #define STATUS_NEWIP 0x13 22 | #define STATUS_UPDATERSSI 0x14 23 | 24 | extern QueueHandle_t updatequeue; 25 | 26 | struct statusstruct { 27 | bool txActive = false; 28 | uint8_t currentmode = 0; 29 | bool rxActive = false; 30 | bool rxSignal = false; 31 | uint32_t framesReceived = 0; 32 | uint32_t freq = 0; 33 | TaskHandle_t websocketUpdater; 34 | TaskHandle_t ledupdater; 35 | TaskHandle_t lcdupdater; 36 | TaskHandle_t updater; 37 | }; 38 | 39 | extern struct statusstruct status; 40 | 41 | void statustrackerTask(void *parameter); 42 | void sendStatus(uint32_t statusval); 43 | void sendStatusISR(uint32_t statusval); -------------------------------------------------------------------------------- /ESP32-firmware/include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/vias.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INVias*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10C,1.152400*% 12 | %ADD11C,0.552400*% 13 | %ADD12C,0.952400*% 14 | 15 | 16 | D10* 17 | X349250Y177800D03* 18 | D11* 19 | X158750Y165100D03* 20 | X222250Y101600D03* 21 | X127000Y469900D03* 22 | X146050Y755650D03* 23 | X260350Y730250D03* 24 | X285750Y679450D03* 25 | X120650Y679450D03* 26 | X101600Y622300D03* 27 | X101600Y577850D03* 28 | X254000Y609600D03* 29 | X266700Y628650D03* 30 | X311150Y609600D03* 31 | X336550Y628650D03* 32 | X209550Y571500D03* 33 | X228600Y584200D03* 34 | X101600Y342900D03* 35 | X82550Y285750D03* 36 | X152400Y285750D03* 37 | X19050Y120650D03* 38 | X19050Y69850D03* 39 | X171450Y63500D03* 40 | X95250Y25400D03* 41 | X95250Y95250D03* 42 | X171450Y120650D03* 43 | X196850Y25400D03* 44 | X323850Y31750D03* 45 | X349250Y209550D03* 46 | X444500Y215900D03* 47 | X349250Y279400D03* 48 | X412750Y304800D03* 49 | X393700Y247650D03* 50 | X438150Y361950D03* 51 | X368300Y425450D03* 52 | X412750Y457200D03* 53 | X508000Y317500D03* 54 | X508000Y349250D03* 55 | X69850Y368300D03* 56 | X19050Y241300D03* 57 | X19050Y215900D03* 58 | X76200Y241300D03* 59 | X76200Y215900D03* 60 | D10* 61 | X368300Y628650D03* 62 | X95250Y647700D03* 63 | D12* 64 | X376650Y137250D03* 65 | D11* 66 | X153125Y311875D03* 67 | X88900Y438150D03* 68 | X88900Y450850D03* 69 | X319532Y379984D03* 70 | X423164Y591566D03* 71 | X302260Y358394D03* 72 | X328168Y500888D03* 73 | X487934Y319532D03* 74 | X431800Y574294D03* 75 | X332486Y734060D03* 76 | X112268Y397256D03* 77 | X263398Y423164D03* 78 | X414528Y582930D03* 79 | M02* 80 | -------------------------------------------------------------------------------- /ESP32-firmware/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32cam] 12 | platform = espressif32 13 | board = esp32cam 14 | framework = arduino 15 | board_build.partitions = min_spiffs.csv 16 | board_build.filesystem = littlefs 17 | platform_packages = 18 | framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git 19 | monitor_filters = esp32_exception_decoder 20 | monitor_speed = 115200 21 | board_build.f_cpu = 240000000L 22 | build_unflags = -Os 23 | lib_deps = 24 | FastLED 25 | https://github.com/yubox-node-org/ESPAsyncWebServer 26 | https://github.com/devyte/ESPAsyncDNSServer 27 | bodmer/TFT_eSPI 28 | https://github.com/jjwbruijn/ESP32TimerInterrupt.git 29 | https://github.com/hunamizawa/ESPPerfectTime.git 30 | bblanchon/ArduinoJson 31 | khoih-prog/ESPAsync_WiFiManager 32 | build_flags = 33 | -O3 34 | -DUSER_SETUP_LOADED=1 35 | -DST7789_DRIVER=1 36 | -DTFT_WIDTH=135 37 | -DTFT_HEIGHT=240 38 | -DCGRAM_OFFSET=1 39 | -DTFT_MOSI=19 40 | -DTFT_SCLK=18 41 | -DTFT_CS=5 42 | -DTFT_DC=16 43 | -DTFT_RST=23 44 | -DTFT_BL=4 45 | -DTFT_BACKLIGHT_ON=HIGH 46 | -DLOAD_GLCD=1 47 | -DLOAD_FONT2=1 48 | -DLOAD_FONT4=1 49 | -DLOAD_FONT6=1 50 | -DLOAD_FONT7=1 51 | -DLOAD_FONT8=1 52 | -DLOAD_GFXFF=1 53 | -DSMOOTH_FONT=1 54 | -DSPI_FREQUENCY=40000000 55 | -DSPI_READ_FREQUENCY=6000000 56 | 57 | extra_scripts = ./replace_fs.py -------------------------------------------------------------------------------- /ESP32-firmware/data/www/main.js: -------------------------------------------------------------------------------- 1 | $.fn.htmlTo = function(elem) { 2 | return this.each(function() { 3 | $(elem).html($(this).html()); 4 | }); 5 | } 6 | 7 | let url = 'ws://10.0.15.178/ws'; //localhost?mischien ergens vandaan halen? 8 | let socket = new WebSocket(url); 9 | 10 | socket.onmessage = function(event) { 11 | let incomingMessage = event.data; 12 | showMessage(incomingMessage); 13 | }; 14 | 15 | socket.onclose = event => console.log(`Closed ${event.code}`); 16 | 17 | function showMessage(message) { 18 | try { 19 | let data = JSON.parse(message); 20 | if(data.frames) { 21 | var blocks = []; 22 | $.each( data.frames, function(key, val) { 23 | 24 | var freq = "--"; 25 | if(val.freq !== 0) { 26 | var freq = val.freq / 1000000 + "MHz"; 27 | } 28 | if(val.txType) { 29 | var type = val.txType; 30 | var c = 'txt'; 31 | } 32 | if(val.rxType) { 33 | var type = val.rxType; 34 | var c = 'rxt'; 35 | } 36 | blocks.push( "
  • " + val.frame + "
    " + type + "
    " + freq + "
  • " ); 37 | }); 38 | 39 | } else { 40 | 41 | if(data.frame) { 42 | //data.frame, data.cycle, data.type 43 | newDate = new Date(data.time * 1000); 44 | var date = newDate.toLocaleString(); 45 | var t = date.split(" "); 46 | var time = t[1]; 47 | if(data.messages !== 0) { 48 | var items = []; 49 | $.each(data.messages, function(k, v) { 50 | if(v.msg.length) { 51 | items.push("
    " + time + "
    " + v.msg + "
    "); 52 | $(".pending").remove(); 53 | $( "
  • ", { 54 | "class": "new", 55 | html: items.join("") 56 | }).prependTo( "ul.messages" ); 57 | } 58 | }); 59 | 60 | } 61 | } 62 | } 63 | 64 | } catch(err) { 65 | 66 | 67 | } 68 | 69 | if(blocks) { 70 | $( "
      ", { 71 | "class": "", 72 | html: blocks.join("") 73 | }).htmlTo( ".blocks ul" ); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/DrillFiles/drills.xln: -------------------------------------------------------------------------------- 1 | M48 2 | ;GenerationSoftware,Autodesk,EAGLE,9.6.2*% 3 | ;CreationDate,2020-10-23T10:14:48Z*% 4 | FMAT,2 5 | ICI,OFF 6 | METRIC,TZ,000.000 7 | T7C0.400 8 | T6C0.800 9 | T5C1.000 10 | T4C1.100 11 | T3C1.200 12 | T2C1.270 13 | T1C1.778 14 | % 15 | G90 16 | M71 17 | T1 18 | X51445Y2530 19 | X46345Y2530 20 | X46345Y7630 21 | X51445Y7630 22 | T2 23 | X48895Y5080 24 | T3 25 | X4455Y47860 26 | X4455Y51360 27 | X4445Y40640 28 | X4445Y44140 29 | T4 30 | X14605Y55245 31 | X19685Y55245 32 | X24765Y55245 33 | X27305Y55245 34 | X29845Y55245 35 | X32385Y55245 36 | X34925Y55245 37 | X22225Y55245 38 | X9525Y78545 39 | X12065Y78545 40 | X14605Y78545 41 | X27305Y78545 42 | X29845Y78545 43 | X32385Y78545 44 | X34925Y78545 45 | X37465Y78545 46 | X17145Y78545 47 | X19685Y78545 48 | X17145Y55245 49 | X24765Y78545 50 | X22225Y78545 51 | X9525Y55245 52 | X12065Y55245 53 | X37465Y55245 54 | T5 55 | X10160Y31750 56 | X10160Y36830 57 | X2540Y36830 58 | X2540Y31750 59 | X2540Y26670 60 | X2540Y19050 61 | X9525Y64770 62 | X36830Y62865 63 | X34925Y17780 64 | X10160Y26670 65 | X10160Y19050 66 | T6 67 | X37665Y13725 68 | T7 69 | X15312Y31187 70 | X8890Y43815 71 | X8890Y45085 72 | X31953Y37998 73 | X31115Y60960 74 | X30226Y35839 75 | X32817Y50089 76 | X48793Y31953 77 | X43180Y57429 78 | X33249Y73406 79 | X11227Y39726 80 | X26340Y42316 81 | X41453Y58293 82 | X15875Y16510 83 | X22225Y10160 84 | X12700Y46990 85 | X42316Y59157 86 | X14605Y75565 87 | X26035Y73025 88 | X28575Y67945 89 | X12065Y67945 90 | X10160Y62230 91 | X10160Y57785 92 | X25400Y60960 93 | X26670Y62865 94 | X33655Y62865 95 | X20955Y57150 96 | X22860Y58420 97 | X10160Y34290 98 | X8255Y28575 99 | X15240Y28575 100 | X1905Y12065 101 | X1905Y6985 102 | X17145Y6350 103 | X9525Y2540 104 | X9525Y9525 105 | X17145Y12065 106 | X19685Y2540 107 | X32385Y3175 108 | X34925Y20955 109 | X44450Y21590 110 | X6985Y36830 111 | X34925Y27940 112 | X41275Y30480 113 | X39370Y24765 114 | X43815Y36195 115 | X36830Y42545 116 | X41275Y45720 117 | X50800Y31750 118 | X50800Y34925 119 | X1905Y24130 120 | X1905Y21590 121 | X7620Y24130 122 | X7620Y21590 123 | M30 -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/DrillFiles/drill_1_16.xln: -------------------------------------------------------------------------------- 1 | M48 2 | ;GenerationSoftware,Autodesk,EAGLE,9.6.2*% 3 | ;CreationDate,2020-10-23T10:40:54Z*% 4 | FMAT,2 5 | ICI,OFF 6 | METRIC,TZ,000.000 7 | T7C0.400 8 | T6C0.800 9 | T5C1.000 10 | T4C1.100 11 | T3C1.200 12 | T2C1.270 13 | T1C1.778 14 | % 15 | G90 16 | M71 17 | T1 18 | X51445Y2530 19 | X46345Y2530 20 | X46345Y7630 21 | X51445Y7630 22 | T2 23 | X48895Y5080 24 | T3 25 | X4455Y47860 26 | X4455Y51360 27 | X4445Y40640 28 | X4445Y44140 29 | T4 30 | X14605Y55245 31 | X19685Y55245 32 | X24765Y55245 33 | X27305Y55245 34 | X29845Y55245 35 | X32385Y55245 36 | X34925Y55245 37 | X22225Y55245 38 | X9525Y78545 39 | X12065Y78545 40 | X14605Y78545 41 | X27305Y78545 42 | X29845Y78545 43 | X32385Y78545 44 | X34925Y78545 45 | X37465Y78545 46 | X17145Y78545 47 | X19685Y78545 48 | X17145Y55245 49 | X24765Y78545 50 | X22225Y78545 51 | X9525Y55245 52 | X12065Y55245 53 | X37465Y55245 54 | T5 55 | X10160Y31750 56 | X10160Y36830 57 | X2540Y36830 58 | X2540Y31750 59 | X2540Y26670 60 | X2540Y19050 61 | X9525Y64770 62 | X36830Y62865 63 | X34925Y17780 64 | X10160Y26670 65 | X10160Y19050 66 | T6 67 | X37665Y13725 68 | T7 69 | X15312Y31187 70 | X8890Y43815 71 | X8890Y45085 72 | X31953Y37998 73 | X31115Y60960 74 | X30226Y35839 75 | X32817Y50089 76 | X48793Y31953 77 | X43180Y57429 78 | X33249Y73406 79 | X11227Y39726 80 | X26340Y42316 81 | X41453Y58293 82 | X15875Y16510 83 | X22225Y10160 84 | X12700Y46990 85 | X42316Y59157 86 | X14605Y75565 87 | X26035Y73025 88 | X28575Y67945 89 | X12065Y67945 90 | X10160Y62230 91 | X10160Y57785 92 | X25400Y60960 93 | X26670Y62865 94 | X33655Y62865 95 | X20955Y57150 96 | X22860Y58420 97 | X10160Y34290 98 | X8255Y28575 99 | X15240Y28575 100 | X1905Y12065 101 | X1905Y6350 102 | X17145Y6350 103 | X9525Y4445 104 | X9525Y9525 105 | X17145Y12065 106 | X19685Y1270 107 | X32385Y3175 108 | X34925Y20955 109 | X44450Y21590 110 | X6985Y36830 111 | X34925Y27940 112 | X41275Y30480 113 | X39370Y24765 114 | X43815Y36195 115 | X36830Y42545 116 | X41275Y45720 117 | X50800Y31750 118 | X50800Y34925 119 | X1905Y24130 120 | X1905Y21590 121 | X7620Y24130 122 | X7620Y21590 123 | M30 -------------------------------------------------------------------------------- /ESP32-firmware/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "BCH3121.h" 6 | #include "RF4463.h" 7 | #include "WiFi.h" 8 | #include "jwifi.h" 9 | #include "lcd.h" 10 | #include "led.h" 11 | #include "statustracker.h" 12 | 13 | #define DEBUG 14 | #define SERDEBUG 15 | #include "bitutils.h" 16 | #include "decoder.h" 17 | #include "encoder.h" 18 | #include "flexutils.h" 19 | #include "httpd.h" 20 | #include "pocsagutils.h" 21 | #include "settings.h" 22 | #include "soc/rtc_wdt.h" 23 | 24 | RF4463 rf4463; 25 | 26 | extern void initRF(void); 27 | 28 | char buff[512]; 29 | 30 | void processOTAUpdate(void); 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | 35 | ets_printf("class/struct size\n"); 36 | ets_printf("--------------------------------\n"); 37 | ets_printf("flexbiw: %u\n", sizeof(flexbiw)); 38 | ets_printf("flexsync: %u\n", sizeof(flexsync)); 39 | ets_printf("flexfiw: %u\n", sizeof(flexfiw)); 40 | ets_printf("flexaddress: %u\n", sizeof(flexaddress)); 41 | ets_printf("flextempaddrmapping: %u\n", sizeof(flextempaddrmapping)); 42 | ets_printf("flexvector: %u\n", sizeof(flexvector)); 43 | ets_printf("flexphase: %u\n", sizeof(flexphase)); 44 | ets_printf("flexdatablock: %u\n", sizeof(flexdatablock)); 45 | ets_printf("flexmsg: %u\n", sizeof(flexmsg)); 46 | ets_printf("flexframe: %u\n", sizeof(flexframe)); 47 | ets_printf("--------------------------------\n"); 48 | 49 | rf4463.powerOnReset(); 50 | setupSettings(); 51 | xTaskCreate(lcdTask, "LCD Process", 5000, NULL, 2, NULL); 52 | xTaskCreate(ledTask, "LED Process", 5000, NULL, 2, NULL); 53 | xTaskCreate(statustrackerTask, "statustrackerTask", 5000, NULL, 2, NULL); 54 | xTaskCreate(wifiProcess, "WiFi Process", 3000, NULL, 2, NULL); 55 | while (!wifiConnected) { 56 | } 57 | xTaskCreate(httpdProcess, "HTTPD Process", 3000, NULL, 4, NULL); 58 | xTaskCreate(flexTask, "flexTask", 3000, NULL, configMAX_PRIORITIES - 2, NULL); 59 | xTaskCreate(pocsagTask, "pocsagTask", 5000, NULL, 5, NULL); 60 | xTaskCreate(syncTask, "syncTask", 4000, NULL, 2, NULL); 61 | } 62 | 63 | void loop() { 64 | //rtc_wdt_feed(); 65 | vTaskDelay(1); 66 | } -------------------------------------------------------------------------------- /ESP32-firmware/include/pocsagutils.h: -------------------------------------------------------------------------------- 1 | #ifndef POCSAGUTILS 2 | #define POCSAGUTILS 3 | #include 4 | 5 | #define SYNCWORD 0x7CD215D8 6 | #define IDLEWORD 0x7A89C197 7 | 8 | class pocsagmsg; 9 | 10 | class pocsagaddr { 11 | public: 12 | enum func { A = 'A', 13 | B = 'B', 14 | C = 'C', 15 | D = 'D' }; 16 | enum func func; 17 | enum type { TONE, 18 | ALPHA, 19 | NUMERIC }; 20 | enum type type; 21 | 22 | uint32_t ric; 23 | uint8_t frameno; 24 | pocsagaddr(uint32_t addrword, uint8_t word); 25 | pocsagaddr(uint32_t ric, enum type type, enum func function); 26 | pocsagaddr(); 27 | uint32_t decodeAddress(uint32_t word, uint8_t wordcount); 28 | uint32_t getWord(); 29 | }; 30 | 31 | class pocsagdata { 32 | public: 33 | struct frame_s { 34 | uint32_t word[2]; 35 | }; 36 | struct batchs { 37 | struct frame_s frame[8]; 38 | }; 39 | struct batchs* batch = nullptr; 40 | 41 | uint16_t* errorflags; 42 | 43 | enum wordtype { TYPE_IDLE, 44 | TYPE_ADDR, 45 | TYPE_MSG }; 46 | bool msgStarted = false; 47 | uint16_t batchcount = 0; 48 | uint16_t msgwordcount = 0; 49 | uint16_t baudrate = 512; 50 | uint32_t freq = 0; 51 | pocsagdata(); 52 | pocsagdata(pocsagaddr ric, char* data); 53 | ~pocsagdata(); 54 | void addMsg(pocsagaddr ric, char* data); 55 | void growBatches(uint16_t growby); 56 | uint16_t requiredSize(pocsagaddr ric, char* data); 57 | void addWord(uint32_t word, uint8_t wordcount, bool hasErrors); 58 | uint8_t getFrameSize(); 59 | uint8_t getFrameSize(uint16_t batchcount); 60 | void decodeMessage(batchs* batchp, uint8_t batchcount, uint16_t wordcount, uint16_t* errorflagsp); 61 | uint8_t encodeNumber(char c); 62 | char decodeNumber(uint8_t c); 63 | enum wordtype getType(uint32_t word); 64 | bool isMsg(uint32_t word); 65 | bool isAddr(uint32_t word); 66 | bool isIdle(uint32_t word); 67 | static int getRating(char* alpha, char* numeric, pocsagaddr addr); 68 | void queueMessage(pocsagmsg* msg); 69 | }; 70 | 71 | class pocsagmsg { 72 | public: 73 | pocsagaddr addr; 74 | char* alphadata; 75 | char* numdata; 76 | pocsagmsg(pocsagaddr addr, char* alphadata, char* numdata); 77 | ~pocsagmsg(); 78 | }; 79 | #endif -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/solderpaste_top.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSolderpaste Top*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,2.000000X1.500000*% 12 | %ADD11R,2.950000X2.700000*% 13 | %ADD12R,1.300000X1.500000*% 14 | %ADD13R,1.500000X1.300000*% 15 | %ADD14R,2.700000X2.950000*% 16 | %ADD15R,1.270000X1.470000*% 17 | %ADD16R,1.000000X1.651000*% 18 | %ADD17R,1.500000X2.000000*% 19 | %ADD18R,3.800000X2.000000*% 20 | %ADD19R,6.200000X5.400000*% 21 | %ADD20R,1.600000X1.000000*% 22 | %ADD21R,1.803000X1.600000*% 23 | %ADD22R,1.400000X1.000000*% 24 | %ADD23R,1.400000X1.800000*% 25 | 26 | 27 | D10* 28 | X317500Y516650D03* 29 | X317500Y486650D03* 30 | X292100Y516650D03* 31 | X292100Y486650D03* 32 | X266700Y516650D03* 33 | X266700Y486650D03* 34 | D11* 35 | X376650Y69850D03* 36 | X309150Y69850D03* 37 | X446500Y508000D03* 38 | X379000Y508000D03* 39 | D12* 40 | X200050Y241300D03* 41 | X219050Y241300D03* 42 | X200050Y165100D03* 43 | X219050Y165100D03* 44 | X200050Y88900D03* 45 | X219050Y88900D03* 46 | D13* 47 | X222250Y358750D03* 48 | X222250Y339750D03* 49 | D12* 50 | X200050Y317500D03* 51 | X219050Y317500D03* 52 | D10* 53 | X222250Y385050D03* 54 | X222250Y415050D03* 55 | X107950Y440450D03* 56 | X107950Y410450D03* 57 | D14* 58 | X222250Y510000D03* 59 | X222250Y442500D03* 60 | D15* 61 | X48350Y158750D03* 62 | X91350Y158750D03* 63 | D16* 64 | X244350Y291730D03* 65 | X244350Y343270D03* 66 | X276350Y343270D03* 67 | X276350Y291730D03* 68 | X244350Y367930D03* 69 | X244350Y419470D03* 70 | X276350Y419470D03* 71 | X276350Y367930D03* 72 | X244350Y215530D03* 73 | X244350Y267070D03* 74 | X276350Y267070D03* 75 | X276350Y215530D03* 76 | X244350Y139330D03* 77 | X244350Y190870D03* 78 | X276350Y190870D03* 79 | X276350Y139330D03* 80 | X244350Y63130D03* 81 | X244350Y114670D03* 82 | X276350Y114670D03* 83 | X276350Y63130D03* 84 | D15* 85 | X149950Y342900D03* 86 | X192950Y342900D03* 87 | D17* 88 | X181750Y437900D03* 89 | X158750Y437900D03* 90 | X135750Y437900D03* 91 | D18* 92 | X158750Y374900D03* 93 | D19* 94 | X164700Y488950D03* 95 | D20* 96 | X91700Y511750D03* 97 | X91700Y466150D03* 98 | D21* 99 | X158750Y188980D03* 100 | X158750Y217420D03* 101 | X136700Y217420D03* 102 | X136700Y188980D03* 103 | D22* 104 | X116000Y158750D03* 105 | X138000Y168250D03* 106 | X138000Y149250D03* 107 | D23* 108 | X490650Y417250D03* 109 | X490650Y377250D03* 110 | X490650Y337250D03* 111 | X490650Y297250D03* 112 | X490650Y257250D03* 113 | X490650Y217250D03* 114 | X490650Y177250D03* 115 | X490650Y137250D03* 116 | X304650Y417250D03* 117 | X304650Y377250D03* 118 | X304650Y337250D03* 119 | X304650Y297250D03* 120 | X304650Y257250D03* 121 | X304650Y217250D03* 122 | X304650Y177250D03* 123 | X304650Y137250D03* 124 | M02* 125 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/GerberFiles/solderpaste_top.gbr: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSolderpaste Top*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,2.000000X1.500000*% 12 | %ADD11R,2.950000X2.700000*% 13 | %ADD12R,1.300000X1.500000*% 14 | %ADD13R,1.500000X1.300000*% 15 | %ADD14R,2.700000X2.950000*% 16 | %ADD15R,1.270000X1.470000*% 17 | %ADD16R,1.000000X1.651000*% 18 | %ADD17R,1.500000X2.000000*% 19 | %ADD18R,3.800000X2.000000*% 20 | %ADD19R,6.200000X5.400000*% 21 | %ADD20R,1.600000X1.000000*% 22 | %ADD21R,1.803000X1.600000*% 23 | %ADD22R,1.400000X1.000000*% 24 | %ADD23R,1.400000X1.800000*% 25 | 26 | 27 | D10* 28 | X317500Y516650D03* 29 | X317500Y486650D03* 30 | X292100Y516650D03* 31 | X292100Y486650D03* 32 | X266700Y516650D03* 33 | X266700Y486650D03* 34 | D11* 35 | X376650Y69850D03* 36 | X309150Y69850D03* 37 | X446500Y508000D03* 38 | X379000Y508000D03* 39 | D12* 40 | X200050Y241300D03* 41 | X219050Y241300D03* 42 | X200050Y165100D03* 43 | X219050Y165100D03* 44 | X200050Y88900D03* 45 | X219050Y88900D03* 46 | D13* 47 | X222250Y358750D03* 48 | X222250Y339750D03* 49 | D12* 50 | X200050Y317500D03* 51 | X219050Y317500D03* 52 | D10* 53 | X222250Y385050D03* 54 | X222250Y415050D03* 55 | X107950Y440450D03* 56 | X107950Y410450D03* 57 | D14* 58 | X222250Y510000D03* 59 | X222250Y442500D03* 60 | D15* 61 | X48350Y158750D03* 62 | X91350Y158750D03* 63 | D16* 64 | X244350Y291730D03* 65 | X244350Y343270D03* 66 | X276350Y343270D03* 67 | X276350Y291730D03* 68 | X244350Y367930D03* 69 | X244350Y419470D03* 70 | X276350Y419470D03* 71 | X276350Y367930D03* 72 | X244350Y215530D03* 73 | X244350Y267070D03* 74 | X276350Y267070D03* 75 | X276350Y215530D03* 76 | X244350Y139330D03* 77 | X244350Y190870D03* 78 | X276350Y190870D03* 79 | X276350Y139330D03* 80 | X244350Y63130D03* 81 | X244350Y114670D03* 82 | X276350Y114670D03* 83 | X276350Y63130D03* 84 | D15* 85 | X149950Y342900D03* 86 | X192950Y342900D03* 87 | D17* 88 | X181750Y437900D03* 89 | X158750Y437900D03* 90 | X135750Y437900D03* 91 | D18* 92 | X158750Y374900D03* 93 | D19* 94 | X164700Y488950D03* 95 | D20* 96 | X91700Y511750D03* 97 | X91700Y466150D03* 98 | D21* 99 | X158750Y188980D03* 100 | X158750Y217420D03* 101 | X136700Y217420D03* 102 | X136700Y188980D03* 103 | D22* 104 | X116000Y158750D03* 105 | X138000Y168250D03* 106 | X138000Y149250D03* 107 | D23* 108 | X490650Y417250D03* 109 | X490650Y377250D03* 110 | X490650Y337250D03* 111 | X490650Y297250D03* 112 | X490650Y257250D03* 113 | X490650Y217250D03* 114 | X490650Y177250D03* 115 | X490650Y137250D03* 116 | X304650Y417250D03* 117 | X304650Y377250D03* 118 | X304650Y337250D03* 119 | X304650Y297250D03* 120 | X304650Y257250D03* 121 | X304650Y217250D03* 122 | X304650Y177250D03* 123 | X304650Y137250D03* 124 | M02* 125 | -------------------------------------------------------------------------------- /ESP32-firmware/include/settings.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ESPAsyncWebServer.h" 3 | 4 | #ifndef CERT 5 | #define CERT 6 | 7 | extern const uint8_t der[]; 8 | extern size_t derlen; 9 | extern char macStr[13]; 10 | extern String uuid; 11 | 12 | struct settingsstruct { 13 | struct { 14 | bool ppsenabled; 15 | } gps; 16 | struct { 17 | bool loginRequired; 18 | char username[32]; 19 | char password[32]; 20 | char ntpserver[128]; 21 | } webportal; 22 | struct { 23 | bool enabled; 24 | char host[64]; 25 | uint16_t port; 26 | char username[32]; 27 | char password[32]; 28 | } mqtt; 29 | struct { 30 | uint8_t defaultmode; 31 | uint32_t defaultfrequency; 32 | uint16_t defaultbaud; 33 | uint16_t defaultoffset; 34 | uint8_t defaultxmitpower; 35 | } rf; 36 | struct { 37 | uint8_t mode; 38 | uint32_t freq; 39 | uint16_t baud; 40 | bool flexsynced; 41 | } current; 42 | struct { 43 | bool typebymessagecontent; 44 | bool markerrors; 45 | } pocsag; 46 | struct { 47 | bool autosendidleframes; 48 | bool allowSendToRX; 49 | uint8_t collapse; 50 | } flex; 51 | struct { 52 | bool lcdenabled; 53 | uint8_t ledsbrightness; 54 | } indicators; 55 | struct { 56 | char url[128]; 57 | bool useSSL; 58 | } update; 59 | }; 60 | 61 | #define LED_WIFI_CONNECTED 0x01 62 | #define LED_WIFI_PORTAL 0x02 63 | #define LED_WIFI_DISCONNECTED 0x04 64 | #define LED_MQTT_CONNECTED 0x08 65 | #define LED_MQTT_ACTIVITY 0x10 66 | #define LED_MQTT_DISCONNECTED 0x20 67 | #define LED_SYNC_SYNCED_TO_CHANNEL 0x40 68 | #define LED_SYNC_SYNCED_NTP 0x80 69 | #define LED_RF_RX_NOSIGNAL 0x100 70 | #define LED_RF_RX_SIGNAL 0x200 71 | #define LED_RF_TX 0x400 72 | #define LED_RF_OFF 0x800 73 | #define LED_WIFI_ACTIVITY 0x1000 74 | #define LED_SYNC_ACTIVITY 0x2000 75 | #define LED_SYNC_NOSYNC 0x4000 76 | 77 | 78 | #define LCD_MODE_IDLE 0x01 79 | #define LCD_MODE_RX 0x02 80 | #define LCD_MODE_TX 0x04 81 | #define LCD_MODECHANGE 0x08 82 | #define LCD_UPDATERSSI 0x10 83 | #define LCD_FREQCHANGE 0x1000 84 | #define LCD_FRAMECHANGE 0x2000 85 | #define LCD_IPCHANGE 0x4000 86 | 87 | extern struct settingsstruct settings; 88 | 89 | extern void setupSettings(); 90 | extern bool loadJSONFile(); 91 | extern void saveJSONFile(); 92 | extern void getSettings(AsyncWebServerRequest *request); 93 | extern void saveSettings(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); 94 | extern void saveSettingsComplete(AsyncWebServerRequest *request); 95 | extern void getOTACert(AsyncWebServerRequest *request); 96 | extern void uploadOTACert(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); 97 | extern void uploadOTACertComplete(AsyncWebServerRequest *request); 98 | extern void getMode(AsyncWebServerRequest *request); 99 | extern void setMode(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); 100 | 101 | #define MODE_FLEX 0 102 | #define MODE_POCSAG 1 103 | #define MODE_TX_ONLY 2 104 | #define MODE_FLEX_SYNCED 3 105 | 106 | 107 | #endif -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/soldermask_bottom.gbs: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSoldermask Bottom*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,1.703200X2.203200*% 12 | %ADD11R,2.203200X1.703200*% 13 | %ADD12R,2.183200X2.183200*% 14 | %ADD13C,2.183200*% 15 | %ADD14P,2.363074X8X22.500000*% 16 | %ADD15R,2.363200X2.363200*% 17 | %ADD16C,2.363200*% 18 | %ADD17C,2.003200*% 19 | %ADD18C,2.489200*% 20 | %ADD19C,3.403600*% 21 | %ADD20C,1.355600*% 22 | %ADD21C,0.755600*% 23 | %ADD22C,1.155600*% 24 | 25 | 26 | D10* 27 | X376950Y577850D03* 28 | X346950Y577850D03* 29 | X92950Y755650D03* 30 | X122950Y755650D03* 31 | X92950Y730250D03* 32 | X122950Y730250D03* 33 | X376950Y603250D03* 34 | X346950Y603250D03* 35 | D11* 36 | X330200Y143750D03* 37 | X330200Y173750D03* 38 | D10* 39 | X376950Y755650D03* 40 | X346950Y755650D03* 41 | D12* 42 | X95250Y552450D03* 43 | X120650Y552450D03* 44 | D13* 45 | X146050Y552450D03* 46 | X171450Y552450D03* 47 | X196850Y552450D03* 48 | X222250Y552450D03* 49 | X247650Y552450D03* 50 | X273050Y552450D03* 51 | X298450Y552450D03* 52 | D12* 53 | X323850Y552450D03* 54 | X349250Y552450D03* 55 | D14* 56 | X374650Y552450D03* 57 | X95250Y785450D03* 58 | D13* 59 | X120650Y785450D03* 60 | X146050Y785450D03* 61 | X171450Y785450D03* 62 | X196850Y785450D03* 63 | X222250Y785450D03* 64 | X247650Y785450D03* 65 | X273050Y785450D03* 66 | X298450Y785450D03* 67 | X323850Y785450D03* 68 | D12* 69 | X349250Y785450D03* 70 | D14* 71 | X374650Y785450D03* 72 | D15* 73 | X44550Y478600D03* 74 | D16* 75 | X44550Y513600D03* 76 | D15* 77 | X44450Y406400D03* 78 | D16* 79 | X44450Y441400D03* 80 | D17* 81 | X101600Y190500D03* 82 | X101600Y266700D03* 83 | X101600Y317500D03* 84 | X101600Y368300D03* 85 | X25400Y368300D03* 86 | X25400Y317500D03* 87 | X25400Y266700D03* 88 | X25400Y190500D03* 89 | D18* 90 | X488950Y50800D03* 91 | D19* 92 | X463451Y76299D03* 93 | X514449Y76299D03* 94 | X514449Y25301D03* 95 | X463451Y25301D03* 96 | D20* 97 | X349250Y177800D03* 98 | D21* 99 | X158750Y165100D03* 100 | X222250Y101600D03* 101 | X127000Y469900D03* 102 | X146050Y755650D03* 103 | X260350Y730250D03* 104 | X285750Y679450D03* 105 | X120650Y679450D03* 106 | X101600Y622300D03* 107 | X101600Y577850D03* 108 | X254000Y609600D03* 109 | X266700Y628650D03* 110 | X311150Y609600D03* 111 | X336550Y628650D03* 112 | X209550Y571500D03* 113 | X228600Y584200D03* 114 | X101600Y342900D03* 115 | X82550Y285750D03* 116 | X152400Y285750D03* 117 | X19050Y120650D03* 118 | X19050Y69850D03* 119 | X171450Y63500D03* 120 | X95250Y25400D03* 121 | X95250Y95250D03* 122 | X171450Y120650D03* 123 | X196850Y25400D03* 124 | X323850Y31750D03* 125 | X349250Y209550D03* 126 | X444500Y215900D03* 127 | X349250Y279400D03* 128 | X412750Y304800D03* 129 | X393700Y247650D03* 130 | X438150Y361950D03* 131 | X368300Y425450D03* 132 | X412750Y457200D03* 133 | X508000Y317500D03* 134 | X508000Y349250D03* 135 | X69850Y368300D03* 136 | X19050Y241300D03* 137 | X19050Y215900D03* 138 | X76200Y241300D03* 139 | X76200Y215900D03* 140 | D20* 141 | X368300Y628650D03* 142 | X95250Y647700D03* 143 | D22* 144 | X376650Y137250D03* 145 | D21* 146 | X153125Y311875D03* 147 | X88900Y438150D03* 148 | X88900Y450850D03* 149 | X319532Y379984D03* 150 | X423164Y591566D03* 151 | X302260Y358394D03* 152 | X328168Y500888D03* 153 | X487934Y319532D03* 154 | X431800Y574294D03* 155 | X332486Y734060D03* 156 | X112268Y397256D03* 157 | X263398Y423164D03* 158 | X414528Y582930D03* 159 | M02* 160 | -------------------------------------------------------------------------------- /ESP32-firmware/src/bitutils.cpp: -------------------------------------------------------------------------------- 1 | #include "bitutils.h" 2 | 3 | #include 4 | 5 | char IRAM_ATTR bitswitch(char b) { 6 | // a really cool way to reverse bit order. Not sure if it's the fastest, but it works! 7 | b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023; 8 | return (b >> 1); 9 | } 10 | 11 | uint8_t IRAM_ATTR swap(uint8_t b) { 12 | // a really cool way to reverse bit order. Not sure if it's the fastest, but it works! 13 | return (b * 0x0202020202ULL & 0x010884422010ULL) % 1023; 14 | } 15 | 16 | uint16_t IRAM_ATTR swap16(uint16_t b) { 17 | uint8_t high = (uint8_t)(b >> 8); 18 | uint8_t low = (uint8_t)(b & 0x00FF); 19 | high = swap(high); 20 | low = swap(low); 21 | return ((uint16_t)low << 8) | ((uint16_t)high); 22 | } 23 | 24 | uint32_t swap32(uint32_t n) { 25 | n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); 26 | n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); 27 | n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); 28 | n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); 29 | n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); 30 | return n; 31 | } 32 | 33 | uint32_t addValue(uint8_t offset, uint8_t bsize, uint32_t value) { 34 | value = swap32(value & ((1 << bsize) - 1)); 35 | return value >> offset; 36 | } 37 | 38 | uint32_t getValue(uint8_t offset, uint8_t bsize, uint32_t value) { 39 | value = swap32(value << offset); 40 | return value & ((1 << bsize) - 1); 41 | } 42 | 43 | uint8_t IRAM_ATTR countbits(uint32_t val) { 44 | uint8_t count = 0; 45 | for (uint8_t c = 0; c < 32; c++) { 46 | if (val & 0x00000001) count++; 47 | val >>= 1; 48 | } 49 | return count; 50 | } 51 | 52 | uint32_t motchecksum(uint32_t in) { 53 | uint8_t counter; 54 | uint32_t orig = in; 55 | in >>= 4; 56 | counter = swap((uint8_t)in) & 0x01; // 1 bit 57 | in >>= 4; 58 | counter += swap((uint8_t)in) & 0x0F; // 4 bits 59 | in >>= 4; 60 | counter += swap((uint8_t)in) & 0x0F; // 4 bits 61 | in >>= 4; 62 | counter += swap((uint8_t)in) & 0x0F; // 4 bits 63 | in >>= 4; 64 | counter += swap((uint8_t)in) & 0x0F; // 4 bits 65 | counter = ~counter; 66 | counter = swap(counter) & 0xF0; 67 | orig |= ((uint32_t)counter) << 24; 68 | return orig; 69 | } 70 | 71 | bool validateMotChecksum(uint32_t word){ 72 | uint32_t temp = word&0b11111111111111111111100000000000;; 73 | word &= 0b00001111111111111111100000000000; 74 | word = motchecksum(word); 75 | if(word == temp){ 76 | return true; 77 | } else { 78 | return false; 79 | } 80 | }; 81 | 82 | uint32_t IRAM_ATTR createcrc(uint32_t in) { 83 | // I borrowed this routine from Kristoff. Credit goes to him! 84 | // https://github.com/on1arf/rf22_pocsag 85 | // (c) 2014 Kristoff Bonne (ON1ARF) 86 | // 87 | // local vars 88 | uint32_t cw; // codeword 89 | uint32_t local_cw = 0; 90 | uint32_t parity = 0; 91 | // init cw 92 | cw = in; 93 | // move up 11 bits to make place for crc + parity 94 | local_cw = in; /* bch */ 95 | // calculate crc 96 | for (int bit = 1; bit <= 21; bit++, cw <<= 1) { 97 | if (cw & 0x80000000) { 98 | cw ^= 0xED200000; 99 | }; // end if 100 | }; // end for 101 | local_cw |= (cw >> 21); 102 | // parity 103 | cw = local_cw; 104 | for (int bit = 1; bit <= 32; bit++, cw <<= 1) { 105 | if (cw & 0x80000000) { 106 | parity++; 107 | }; // end if 108 | }; // end for 109 | // make even parity 110 | if (parity % 2) { 111 | local_cw++; 112 | }; // end if 113 | // done 114 | return (local_cw); 115 | } -------------------------------------------------------------------------------- /ESP32-firmware/src/led.cpp: -------------------------------------------------------------------------------- 1 | #include "led.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "settings.h" 7 | #include "statustracker.h" 8 | 9 | FASTLED_USING_NAMESPACE 10 | //#define FASTLED_FORCE_SOFTWARE_SPI 11 | 12 | #define NUM_LEDS 5 13 | #define DATA_PIN 22 14 | 15 | #define LED_POWER 0 16 | #define LED_WIFI 1 17 | #define LED_MQTT 2 18 | #define LED_SYNC 3 19 | #define LED_RF 4 20 | #define LED_OFF CRGB::Black 21 | 22 | CRGB leds[NUM_LEDS]; 23 | 24 | void ledSetup() { 25 | FastLED.addLeds(leds, NUM_LEDS); 26 | leds[LED_POWER] = CRGB::Green; 27 | leds[LED_WIFI] = LED_OFF; 28 | leds[LED_MQTT] = LED_OFF; 29 | leds[LED_SYNC] = LED_OFF; 30 | leds[LED_RF] = LED_OFF; 31 | FastLED.setBrightness(settings.indicators.ledsbrightness); 32 | FastLED.show(); 33 | } 34 | 35 | void ledTask(void *parameter) { 36 | ledSetup(); 37 | status.ledupdater = xTaskGetCurrentTaskHandle(); 38 | bool wifiActivity = false; 39 | bool mqttActivity = false; 40 | bool syncActivity = false; 41 | bool syncNTP = false; 42 | bool syncChannel = false; 43 | uint32_t action = 0; 44 | while (true) { 45 | if (wifiActivity || mqttActivity || syncActivity) { 46 | action = ulTaskNotifyTake(pdTRUE, 25 / portTICK_RATE_MS); 47 | } else { 48 | action = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 49 | } 50 | if (action == 0) { //timeout 51 | if (wifiActivity) { 52 | leds[LED_WIFI] = CRGB::Green; 53 | wifiActivity = false; 54 | FastLED.show(); 55 | } 56 | if (mqttActivity) { 57 | leds[LED_MQTT] = CRGB::Green; 58 | mqttActivity = false; 59 | FastLED.show(); 60 | } 61 | if (syncActivity) { 62 | if (syncNTP) 63 | leds[LED_SYNC] = CRGB::Green; 64 | if (syncChannel) 65 | leds[LED_SYNC] = CRGB::White; 66 | syncActivity = false; 67 | FastLED.show(); 68 | } 69 | } else { 70 | if (action & LED_WIFI_CONNECTED) leds[LED_WIFI] = CRGB::Green; 71 | if (action & LED_WIFI_DISCONNECTED) leds[LED_WIFI] = CRGB::DarkRed; 72 | if (action & LED_WIFI_PORTAL) leds[LED_WIFI] = CRGB::DarkBlue; 73 | if (action & LED_WIFI_ACTIVITY) { 74 | leds[LED_WIFI] = CRGB::Black; 75 | wifiActivity = true; 76 | } 77 | if (action & LED_MQTT_CONNECTED) leds[LED_MQTT] = CRGB::Green; 78 | if (action & LED_MQTT_DISCONNECTED) leds[LED_MQTT] = CRGB::Red; 79 | if (action & LED_MQTT_ACTIVITY) { 80 | leds[LED_MQTT] = CRGB::Black; 81 | mqttActivity = true; 82 | } 83 | if (action & LED_SYNC_NOSYNC) { 84 | leds[LED_SYNC] = CRGB::Black; 85 | syncNTP = false; 86 | syncChannel = false; 87 | syncActivity = false; 88 | } 89 | if (action & LED_SYNC_SYNCED_NTP) { 90 | leds[LED_SYNC] = CRGB::Green; 91 | syncNTP = true; 92 | } 93 | if (action & LED_SYNC_SYNCED_TO_CHANNEL) { 94 | leds[LED_SYNC] = CRGB::Yellow; 95 | syncChannel = true; 96 | } 97 | if (action & LED_SYNC_ACTIVITY) { 98 | leds[LED_SYNC] = CRGB::Black; 99 | syncActivity = true; 100 | } 101 | if (action & LED_RF_RX_NOSIGNAL) leds[LED_RF] = CRGB::Green; 102 | if (action & LED_RF_RX_SIGNAL) leds[LED_RF] = CRGB::Blue; 103 | if (action & LED_RF_TX) leds[LED_RF] = CRGB::Yellow; 104 | if (action & LED_RF_OFF) leds[LED_RF] = CRGB::Black; 105 | 106 | FastLED.show(); 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /ESP32-firmware/data/www/main.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin:0; 3 | padding:0; 4 | border:0; 5 | list-style-type: none; 6 | -webkit-appearance: none; 7 | outline: none; 8 | font-weight: 400; 9 | -webkit-box-sizing: border-box; 10 | -moz-box-sizing: border-box; 11 | box-sizing: border-box; 12 | font-smooth: auto; 13 | -webkit-font-smoothing: antialiased; 14 | } 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | body { 20 | font-size: 12px; 21 | font-family: Helvetica, Arial, Verdana, sans-serif; 22 | line-height: 1.5; 23 | background-color: #e8f1f9; 24 | background-color: #f1f1f1; 25 | /*background-image: linear-gradient(315deg, #dde1ee 0%, #e8f1f9 100%);*/ 26 | } 27 | 28 | header { 29 | height: 50px; 30 | background-color: #666; 31 | 32 | } 33 | .logo { 34 | margin: 0 auto; 35 | width: 50px; 36 | height: 50px; 37 | text-indent: 100px; 38 | overflow:hidden; 39 | background: url('p2000.svg') center center no-repeat; 40 | background-size: 50px 50px; 41 | } 42 | 43 | .container { 44 | 45 | } 46 | 47 | .blocks { 48 | padding: 21px 0; 49 | overflow-x: auto; 50 | text-align:center; 51 | } 52 | .blocks ul { 53 | display:table; 54 | margin: 0 auto; 55 | } 56 | .blocks ul li { 57 | display:table-cell; 58 | vertical-align:top; 59 | padding: 0 4px; 60 | position: relative; 61 | } 62 | .blocks ul li:nth-child(6) div.b:before, .blocks ul li:nth-child(6) div.b:after { 63 | content:""; 64 | position:absolute; 65 | margin-left: -5px; 66 | left: 50%; 67 | width: 0; 68 | height: 0; 69 | } 70 | .blocks ul li:nth-child(6) div.b:before { 71 | top: -5px; 72 | border-left: 5px solid transparent; 73 | border-right: 5px solid transparent; 74 | border-top: 5px solid #c30; 75 | } 76 | .blocks ul li:nth-child(6) div.b:after { 77 | bottom: -5px; 78 | border-left: 5px solid transparent; 79 | border-right: 5px solid transparent; 80 | border-bottom: 5px solid #c30; 81 | } 82 | .blocks ul li, .blocks ul li div { 83 | 84 | } 85 | .blocks ul li div { 86 | width: 80px; 87 | padding: 0 4px; 88 | 89 | } 90 | .blocks ul li div.b { 91 | position:relative; 92 | padding: 6px 0; 93 | background: #fff; 94 | } 95 | .blocks ul li div.rxt { 96 | background-color: #e5f9ea; 97 | } 98 | .blocks ul li div.txt { 99 | background-color: #f9eae5; 100 | } 101 | .blocks ul li div.b { 102 | 103 | } 104 | .window { 105 | margin: 0 auto; 106 | max-width: 94%; 107 | background: #fff; 108 | } 109 | 110 | 111 | ul.messages { 112 | padding: 12px 0; 113 | } 114 | ul.messages li { 115 | line-height: 22px; 116 | position: relative; 117 | padding: 0 12px; 118 | } 119 | ul.messages li.new { 120 | -webkit-animation-name: new; 121 | -webkit-animation-duration: 1400ms; 122 | -webkit-animation-iteration-count: 1; 123 | -webkit-animation-timing-function: ease-in-out; 124 | } 125 | 126 | ul.messages li div.message { 127 | padding-left: 58px; 128 | } 129 | ul.messages li div.date { 130 | position: absolute; 131 | left: 12px 132 | } 133 | span.pending { 134 | padding-left: 28px; 135 | } 136 | span.pending:before { 137 | content:""; 138 | position:absolute; 139 | display:inline-block; 140 | top: 0; 141 | left: 12px; 142 | width: 22px; 143 | height: 22px; 144 | background: url('roll.svg') center center no-repeat; 145 | background-size: 22px 22px; 146 | } 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | @media(max-width: 460px) { 155 | .messages li div, ul.messages li div.date, ul.messages li div.message { 156 | display:block; 157 | position:relative; 158 | padding: 0; 159 | left: auto; 160 | } 161 | .messages li div.message, li.pending { 162 | margin-bottom: 8px; 163 | } 164 | ul.messages { 165 | padding-bottom: 4px; 166 | } 167 | } 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | @-webkit-keyframes new { 177 | 0% { 178 | background-color: rgba(255, 255, 204, 1); 179 | } 180 | 50% { 181 | background-color: rgba(255, 255, 204, .5); 182 | } 183 | 100% { 184 | background-color: rgba(255, 255, 204, 0); 185 | } 186 | } -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/CAMOutputs/Assembly/main-single-esp32.txt: -------------------------------------------------------------------------------- 1 | Partlist exported from Z:/Documents/Documenten Jelmer/projects/eagle/NewPocsag/main-single-esp32.sch at 23-10-2020 12:40 2 | 3 | Qty Value Device Package Parts Description HEIGHT MF MPN OC_FARNELL OC_NEWARK POPULARITY PROD_ID SPICEPREFIX 4 | 2 DIODESMA SMA-DIODE D1, D7 Diode 5 | 2 M023.5MM-NO_SILK SCREWTERMINAL-3.5MM-2-NS J1, J2 Standard 2-pin 0.1" header. Use with CONN-08399 6 | 1 MMBT2222ALT1-NPN-SOT23-BEC SOT23-BEC T1 NPN Transistror 0 7 | 5 WS2812B WS2812B D2, D3, D4, D5, D6 WS2812B - Intelligent control LED integrated light source DIO-12503 8 | 1 10k R-EU_R1206 R1206 R9 RESISTOR, European symbol 9 | 7 10µF C-EUC1206K C1206K C3, C4, C5, C6, C10, C11, C12 CAPACITOR, European symbol 13 C 10 | 1 12v CPOL-EUD/7343-31W D/7343-31W C9 POLARIZED CAPACITOR, European symbol 1 C 11 | 5 1µF C-EUC0805 C0805 C15, C16, C17, C18, C19 CAPACITOR, European symbol 88 C 12 | 4 1µF C-EUC1206K C1206K C1, C2, C7, C8 CAPACITOR, European symbol 13 C 13 | 1 2k R-EU_R1206 R1206 R8 RESISTOR, European symbol 14 | 1 3v3 CPOL-EUD/7343-31W D/7343-31W C14 POLARIZED CAPACITOR, European symbol 1 C 15 | 1 5v CPOL-EUD/7343-31W D/7343-31W C13 POLARIZED CAPACITOR, European symbol 1 C 16 | 1 7805DT 7805DT TO252 IC3 Positive VOLTAGE REGULATOR 0 17 | 1 BT-5-S BT-5-S BT-XX-S K1 KLEIN RELAIS WASCHDICHT 2 Wechsler unknown unknown 0 18 | 1 BU-SMA-V BU-SMA-V BU-SMA-V X1 FEMALE SMA CONNECTOR unknown unknown 6 19 | 1 LD117AS33TR LD117AS33TR SOT223 IC1 Low drop fixed and adjustable positive voltage regulators 1 A 9 20 | 1 RF4463F30 RF4463F30 NICERF_16PIN_SPI_TRANSCEIVER U$1 21 | 1 TTGO_DISP TTGO_DISP TTGO_DISP_DEFAULT IC2 11 22 | -------------------------------------------------------------------------------- /ESP32-firmware/data/www/p2000.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/GerberFiles/soldermask_top.gts: -------------------------------------------------------------------------------- 1 | G04 EAGLE Gerber RS-274X export* 2 | G75* 3 | %MOMM*% 4 | %FSLAX34Y34*% 5 | %LPD*% 6 | %INSoldermask Top*% 7 | %IPPOS*% 8 | %AMOC8* 9 | 5,1,8,0,0,1.08239X$1,22.5*% 10 | G01* 11 | %ADD10R,2.203200X1.703200*% 12 | %ADD11R,3.153200X2.903200*% 13 | %ADD12R,1.503200X1.703200*% 14 | %ADD13R,1.703200X1.503200*% 15 | %ADD14R,2.903200X3.153200*% 16 | %ADD15R,1.473200X1.673200*% 17 | %ADD16R,1.203200X1.854200*% 18 | %ADD17R,1.703200X2.203200*% 19 | %ADD18R,4.003200X2.203200*% 20 | %ADD19R,2.183200X2.183200*% 21 | %ADD20C,2.183200*% 22 | %ADD21P,2.363074X8X22.500000*% 23 | %ADD22R,6.403200X5.603200*% 24 | %ADD23R,1.803200X1.203200*% 25 | %ADD24R,2.363200X2.363200*% 26 | %ADD25C,2.363200*% 27 | %ADD26C,2.003200*% 28 | %ADD27R,2.006200X1.803200*% 29 | %ADD28R,1.603200X1.203200*% 30 | %ADD29R,1.603200X2.003200*% 31 | %ADD30C,2.489200*% 32 | %ADD31C,3.403600*% 33 | %ADD32C,1.355600*% 34 | %ADD33C,0.755600*% 35 | %ADD34C,1.155600*% 36 | 37 | 38 | D10* 39 | X317500Y516650D03* 40 | X317500Y486650D03* 41 | X292100Y516650D03* 42 | X292100Y486650D03* 43 | X266700Y516650D03* 44 | X266700Y486650D03* 45 | D11* 46 | X376650Y69850D03* 47 | X309150Y69850D03* 48 | X446500Y508000D03* 49 | X379000Y508000D03* 50 | D12* 51 | X200050Y241300D03* 52 | X219050Y241300D03* 53 | X200050Y165100D03* 54 | X219050Y165100D03* 55 | X200050Y88900D03* 56 | X219050Y88900D03* 57 | D13* 58 | X222250Y358750D03* 59 | X222250Y339750D03* 60 | D12* 61 | X200050Y317500D03* 62 | X219050Y317500D03* 63 | D10* 64 | X222250Y385050D03* 65 | X222250Y415050D03* 66 | X107950Y440450D03* 67 | X107950Y410450D03* 68 | D14* 69 | X222250Y510000D03* 70 | X222250Y442500D03* 71 | D15* 72 | X48350Y158750D03* 73 | X91350Y158750D03* 74 | D16* 75 | X244350Y291730D03* 76 | X244350Y343270D03* 77 | X276350Y343270D03* 78 | X276350Y291730D03* 79 | X244350Y367930D03* 80 | X244350Y419470D03* 81 | X276350Y419470D03* 82 | X276350Y367930D03* 83 | X244350Y215530D03* 84 | X244350Y267070D03* 85 | X276350Y267070D03* 86 | X276350Y215530D03* 87 | X244350Y139330D03* 88 | X244350Y190870D03* 89 | X276350Y190870D03* 90 | X276350Y139330D03* 91 | X244350Y63130D03* 92 | X244350Y114670D03* 93 | X276350Y114670D03* 94 | X276350Y63130D03* 95 | D15* 96 | X149950Y342900D03* 97 | X192950Y342900D03* 98 | D17* 99 | X181750Y437900D03* 100 | X158750Y437900D03* 101 | X135750Y437900D03* 102 | D18* 103 | X158750Y374900D03* 104 | D19* 105 | X95250Y552450D03* 106 | X120650Y552450D03* 107 | D20* 108 | X146050Y552450D03* 109 | X171450Y552450D03* 110 | X196850Y552450D03* 111 | X222250Y552450D03* 112 | X247650Y552450D03* 113 | X273050Y552450D03* 114 | X298450Y552450D03* 115 | D19* 116 | X323850Y552450D03* 117 | X349250Y552450D03* 118 | D21* 119 | X374650Y552450D03* 120 | X95250Y785450D03* 121 | D20* 122 | X120650Y785450D03* 123 | X146050Y785450D03* 124 | X171450Y785450D03* 125 | X196850Y785450D03* 126 | X222250Y785450D03* 127 | X247650Y785450D03* 128 | X273050Y785450D03* 129 | X298450Y785450D03* 130 | X323850Y785450D03* 131 | D19* 132 | X349250Y785450D03* 133 | D21* 134 | X374650Y785450D03* 135 | D22* 136 | X164700Y488950D03* 137 | D23* 138 | X91700Y511750D03* 139 | X91700Y466150D03* 140 | D24* 141 | X44550Y478600D03* 142 | D25* 143 | X44550Y513600D03* 144 | D24* 145 | X44450Y406400D03* 146 | D25* 147 | X44450Y441400D03* 148 | D26* 149 | X101600Y190500D03* 150 | X101600Y266700D03* 151 | X101600Y317500D03* 152 | X101600Y368300D03* 153 | X25400Y368300D03* 154 | X25400Y317500D03* 155 | X25400Y266700D03* 156 | X25400Y190500D03* 157 | D27* 158 | X158750Y188980D03* 159 | X158750Y217420D03* 160 | X136700Y217420D03* 161 | X136700Y188980D03* 162 | D28* 163 | X116000Y158750D03* 164 | X138000Y168250D03* 165 | X138000Y149250D03* 166 | D29* 167 | X490650Y417250D03* 168 | X490650Y377250D03* 169 | X490650Y337250D03* 170 | X490650Y297250D03* 171 | X490650Y257250D03* 172 | X490650Y217250D03* 173 | X490650Y177250D03* 174 | X490650Y137250D03* 175 | X304650Y417250D03* 176 | X304650Y377250D03* 177 | X304650Y337250D03* 178 | X304650Y297250D03* 179 | X304650Y257250D03* 180 | X304650Y217250D03* 181 | X304650Y177250D03* 182 | X304650Y137250D03* 183 | D30* 184 | X488950Y50800D03* 185 | D31* 186 | X463451Y76299D03* 187 | X514449Y76299D03* 188 | X514449Y25301D03* 189 | X463451Y25301D03* 190 | D32* 191 | X349250Y177800D03* 192 | D33* 193 | X158750Y165100D03* 194 | X222250Y101600D03* 195 | X127000Y469900D03* 196 | X146050Y755650D03* 197 | X260350Y730250D03* 198 | X285750Y679450D03* 199 | X120650Y679450D03* 200 | X101600Y622300D03* 201 | X101600Y577850D03* 202 | X254000Y609600D03* 203 | X266700Y628650D03* 204 | X311150Y609600D03* 205 | X336550Y628650D03* 206 | X209550Y571500D03* 207 | X228600Y584200D03* 208 | X101600Y342900D03* 209 | X82550Y285750D03* 210 | X152400Y285750D03* 211 | X19050Y120650D03* 212 | X19050Y69850D03* 213 | X171450Y63500D03* 214 | X95250Y25400D03* 215 | X95250Y95250D03* 216 | X171450Y120650D03* 217 | X196850Y25400D03* 218 | X323850Y31750D03* 219 | X349250Y209550D03* 220 | X444500Y215900D03* 221 | X349250Y279400D03* 222 | X412750Y304800D03* 223 | X393700Y247650D03* 224 | X438150Y361950D03* 225 | X368300Y425450D03* 226 | X412750Y457200D03* 227 | X508000Y317500D03* 228 | X508000Y349250D03* 229 | X69850Y368300D03* 230 | X19050Y241300D03* 231 | X19050Y215900D03* 232 | X76200Y241300D03* 233 | X76200Y215900D03* 234 | D32* 235 | X368300Y628650D03* 236 | X95250Y647700D03* 237 | D34* 238 | X376650Y137250D03* 239 | D33* 240 | X153125Y311875D03* 241 | X88900Y438150D03* 242 | X88900Y450850D03* 243 | X319532Y379984D03* 244 | X423164Y591566D03* 245 | X302260Y358394D03* 246 | X328168Y500888D03* 247 | X487934Y319532D03* 248 | X431800Y574294D03* 249 | X332486Y734060D03* 250 | X112268Y397256D03* 251 | X263398Y423164D03* 252 | X414528Y582930D03* 253 | M02* 254 | -------------------------------------------------------------------------------- /ESP32-firmware/src/ota.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | extern const char* rootCACertificate; 4 | //extern const char* API_KEY; 5 | volatile int contentLength = 0; 6 | volatile bool isValidContentType = false; 7 | 8 | const char* rootCACertificate = 9 | "-----BEGIN CERTIFICATE-----\n" 10 | "-----END CERTIFICATE-----\n"; 11 | 12 | inline String getHeaderValue(String header, String headerName) { 13 | return header.substring(strlen(headerName.c_str())); 14 | } 15 | 16 | void processOTAUpdate(void) { 17 | WiFiClientSecure client; // = new WiFiClientSecure; 18 | client.setCACert(rootCACertificate); 19 | if (!client.connect("OTAHOST", 443)) { 20 | Serial.println("Cannot connect"); 21 | return; 22 | } 23 | 24 | bool redirect = true; 25 | while (redirect) { 26 | client.print(String("GET ") + "/getfirmware HTTP/1.1\r\n"); 27 | client.print(String("Host: OTAHOST\r\n")); 28 | client.print("Cache-Control: no-cache\r\n"); 29 | client.print("User-Agent: ESP32-http-Update\r\n"); 30 | client.print("x-ESP32-Chip-ID: " + String(ESP.getChipRevision())+"\r\n"); 31 | client.print("x-ESP32-STA-MAC: " + WiFi.macAddress()+"\r\n"); 32 | client.print("x-ESP32-AP-MAC: " + WiFi.softAPmacAddress()+"\r\n"); 33 | client.print("x-ESP32-free-space: " + String(ESP.getFreeSketchSpace())+"\r\n"); 34 | client.print("x-ESP32-sketch-size: " + String(ESP.getSketchSize())+"\r\n"); 35 | client.print("x-ESP32-sketch-md5: " + String(ESP.getSketchMD5())+"\r\n"); 36 | client.print("x-ESP32-chip-size: " + String(ESP.getFlashChipSize())+"\r\n"); 37 | client.print("x-ESP32-sdk-version: " + String(ESP.getSdkVersion())+"\r\n"); 38 | client.print("Connection: close\r\n\r\n"); 39 | 40 | unsigned long timeout = millis(); 41 | while (client.available() == 0) { 42 | if (millis() - timeout > 5000) { 43 | Serial.println("Client Timeout !"); 44 | client.stop(); 45 | return; 46 | } 47 | } 48 | 49 | while (client.available()) { 50 | String line = client.readStringUntil('\n'); 51 | // Check if the line is end of headers by removing space symbol 52 | line.trim(); 53 | // if the the line is empty, this is the end of the headers 54 | if (!line.length()) { 55 | break; // proceed to OTA update 56 | } 57 | 58 | // Check allowed HTTP responses 59 | if (line.startsWith("HTTP/1.1")) { 60 | if (line.indexOf("200") > 0) { 61 | //Serial.println("Got 200 status code from server. Proceeding to firmware flashing"); 62 | redirect = false; 63 | } else if (line.indexOf("302") > 0) { 64 | //Serial.println("Got 302 status code from server. Redirecting to the new address"); 65 | redirect = true; 66 | } else if (line.indexOf("304") > 0) { 67 | Serial.println("No update available..."); 68 | client.flush(); 69 | return; 70 | } else { 71 | //Serial.println("Could not get a valid firmware url"); 72 | //Unexptected HTTP response. Retry or skip update? 73 | redirect = false; 74 | } 75 | } 76 | Serial.println(line); 77 | // Checking headers 78 | if (line.startsWith("Content-Length: ")) { 79 | contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); 80 | Serial.println("Got " + String(contentLength) + " bytes from server"); 81 | } 82 | 83 | if (line.startsWith("Content-Type: ")) { 84 | String contentType = getHeaderValue(line, "Content-Type: "); 85 | //Serial.println("Got " + contentType + " payload."); 86 | if (contentType == "application/octet-stream") { 87 | isValidContentType = true; 88 | } 89 | } 90 | } 91 | 92 | // check whether we have everything for OTA update 93 | if (contentLength && isValidContentType) { 94 | if (Update.begin(contentLength)) { 95 | //killThreads(); 96 | Serial.println("Starting Over-The-Air update. This may take some time to complete ..."); 97 | size_t written = Update.writeStream(client); 98 | if (written == contentLength) { 99 | Serial.println("Written : " + String(written) + " successfully"); 100 | } else { 101 | Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); 102 | // Retry?? 103 | } 104 | 105 | if (Update.end()) { 106 | if (Update.isFinished()) { 107 | Serial.println("OTA update has successfully completed. Rebooting ..."); 108 | ESP.restart(); 109 | } else { 110 | Serial.println("Something went wrong! OTA update hasn't been finished properly."); 111 | } 112 | } else { 113 | Serial.println("An error Occurred. Error #: " + String(Update.getError())); 114 | } 115 | } else { 116 | Serial.println("There isn't enough space to start OTA update"); 117 | client.flush(); 118 | } 119 | } else { 120 | Serial.println("There was no valid content in the response from the OTA server!"); 121 | client.flush(); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # POCSAG/FLEX Modem 2 | 3 | A complete project for receiving pager data, and transmitting POCSAG/FLEX data to old pagers. 4 | 5 | Caution: 6 | - TX: Please only use with a dummy load on HAM-bands 7 | - RX: Please don't look at data you're not supposed to receive 8 | 9 | ## Parts required: 10 | - 78M05 11 | - AMS1117-33 12 | - TTGO-T-display ESP32 module 13 | - RF4463F30 module 14 | - Some passives 15 | optional: 16 | - A relay to drive an external PA 17 | - Status LEDS's 18 | 19 | ## How it works 20 | The modem uses a RF4463F30 wireless module (NiceRF) https://www.tindie.com/products/nicerf/rf4463f30-1w-wireless-transceiver-module/ . Inside is an Silicon Labs Si4463 and a PA, and you can use it to broadcast pretty far-ish. Haven't checked the output yet, but it is a pretty cheap module and you may need to add output filters to avoid splatter on other frequencies. The module allows frequencies up to 1050 Mhz, this should cover (nearly?) all pager bands. While the modules may be optimized for specific frequency bands, they will happily transmit outside of their optimum band. Range will be severly reduced though. 21 | 22 | The modem supports: 23 | - POCSAG 512 24 | - POCSAG 1200 25 | - POCSAG 2400 26 | - FLEX 1600 27 | - FLEX 3200/2 28 | - FLEX 3200/4 29 | - FLEX 6400/4 30 | 31 | Technically the module should be capable of transmitting ERMES data too; I haven't implemented this due to the absence of any working ERMES networks, and I don't have any ERMES pagers in my collection. I'd love to implement ReFLEX (two way paging), but I can't find ANY relevant data/protocol info. There's some stuff in Motorola patents, but it's hard to find anything meaningful. 32 | 33 | POCSAG is easy and straightforward; it's not synchronized and the pagers listen regularly. The length of the POCSAG preamble is pretty long, and the pagers will happily sync and receive info. They will display a little antenna symbol if they don't see any pager traffic for some time. 34 | 35 | The Motorola FLEX protocol is a synchronous protocol; this has a major drawback: if a pager is sync'ed to your network, it will remain synced until it times out, not receiving a couple of frames. 36 | This means: 37 | - If you reset your modem (or if it crashes), your pager will probably lose sync. New messages sent will probably not be received by your pager. You could fix this with a GPS receiver with a PPS output, but it has not been implemented yet. 38 | - FLEX pagers do expect a fair amount of traffic. If they don't regularly see traffic in their assigned frames, they will lose sync and usually default to listening until they get synced up to the network again. This will drain your battery rather quickly! 39 | 40 | To help keep pagers happy, the modem will regularly send empty/idle frames on frequencies/frames that have been used to send messages to pagers. 41 | 42 | The modem is capable of syncing-up to existing FLEX pager networks, and send on their frequencies, in their timeslots. I've never tested this, and neither should you. 43 | 44 | ## How to use 45 | - Build the board 46 | - Build/upload firmware and filesystem 47 | - Connect to wifi (The board will run as an AP, connect and enter your wifi details 48 | - Point your browser to the IP shown on the display, enter frequencies (in Hz) and data, and send away! 49 | 50 | For RX purposes there's somewhat of a web-interface, at /index.html, but you'll need to fill in the IP address of the device in main.js. [this is what it looks like](https://user-images.githubusercontent.com/2544995/197303511-72ad286d-d64b-4eff-b078-07b176b526e7.png) 51 | ) 52 | 53 | ## Adding Frequencies 54 | The RF4463 in the modem uses config files generated by the WDS utility ( https://www.silabs.com/documents/login/software/WDS3-Setup.exe ) 55 | Click 'Simulate Radio', and select the Radio Configuration Application for the RF4463. You can use the included XML templates as a starting point, just add the frequency and hit 'Generate source'. Name it following the naming convention used by the other files in the folder, and use the included php-file to generate a rfconfig.h-styled file to use in the firmware. 56 | 57 | Note: Not all frequencies need to be added; if you entered the channel spacing correctly, the firmware will find the channel based on the base frequency setting and spacing. 58 | 59 | ## Tips and tricks 60 | - POCSAG uses 'function' bits A through D to distinguish between different types of messages, but these are mostly to be used on 'tone' pagers. Most numeric pagers expect A, most alphanumeric pagers expect D, and will decode A-coded messages as numeric. Some pagers support folders, and use different function values to sort messages 61 | - Some pagers use special sequences to do OTA configuration. If you ever find out what those sequences are for popular models, please let me know. 62 | - While they're really supposed to, not all FLEX pagers support all FLEX modes. Some only support the 2FSK modes. 63 | 64 | ## Limitations 65 | - It basically hogs an entire ESP32 core during transmission; this is because everything is pretty time-critical 66 | - It's not amazingly stable 67 | - Firmware uses poor, unsafe coding practices. There's probably a ton of memory leaks :) 68 | - Multi-phase FLEX RX on US nation-wide networks will crash the ESP32 pretty fast. I haven't had an awful lot of time to bugfix this, but the firmware isn't too happy to see a ton of traffic 69 | 70 | ## Disclaimer 71 | - I absolutely deny any and all responsibility should you chose to use, sell, lend or loan, or even just look at this software or hardware design. It's not fit for public consumption, and probably not suitable to be used by anyone. You're on your own! 72 | 73 | ## Errata 74 | - The original version has GPIO2 connected to one of the GPIO's on the RF module. For some reason, this causes an issue where the RF module does something weird during boot. Not sure. Just don't connect the ESP32 GPIO2 pin to the main board and you're good. 75 | - The RF4463 needs to be calibrated for proper use. Calibrated value is set in RF4463.cpp at or around line #288. Should be around that value, calibrating it will improve your range by better matching your pager. Haven't found a better way to do it than just transmitting and checking the resulting frequency using an SDR RX 76 | 77 | ## TODO 78 | - Code cleanup 79 | - Web interface 80 | - Web interface 81 | - I am once again asking for a web interface 82 | -------------------------------------------------------------------------------- /ESP32-firmware/src/BCH3121.cpp: -------------------------------------------------------------------------------- 1 | #include "BCH3121.h" 2 | 3 | #include 4 | /* 5 | * Copyright (C) 2018 by Andy Uribe CA6JAU 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* 23 | * This is a BCH(31,21) implementation, designed for POCSAG 24 | * Based on double-error-correcting method for binary BCH codes 25 | * 26 | * More info (math base): 27 | * https://web.stanford.edu/class/ee387/handouts/notes16.pdf 28 | * https://www.ece.jhu.edu/~cooper/ERROR_CONTROL_CODING/06dec.pdf 29 | */ 30 | 31 | // Polynomial form of alpha^i, GF(2^5), using irreducible polynomial: p(X) = X^5 + X^2 + 1 32 | // See http://pnrsolution.org/Datacenter/Vol3/Issue2/250.pdf (Table I) 33 | const int8_t alpha_to[] = {1, 2, 4, 8, 16, 5, 10, 20, 13, 26, 34 | 17, 7, 14, 28, 29, 31, 27, 19, 3, 6, 35 | 12, 24, 21, 15, 30, 25, 23, 11, 22, 9, 36 | 18, 0}; 37 | 38 | // Index of alpha^i: 39 | const int8_t index_of[] = {-1, 0, 1, 18, 2, 5, 19, 11, 3, 29, 40 | 6, 27, 20, 8, 12, 23, 4, 10, 30, 17, 41 | 7, 22, 28, 26, 21, 25, 9, 16, 13, 14, 42 | 24, 15}; 43 | 44 | #define POCSAG_DATA_MASK 0xFFFFF800; 45 | #define POCSAG_BCH_N 31 46 | #define POCSAG_BCH_K 21 47 | 48 | // Polynomial generator g(X) = X^10 + X^9 + X^8 + X^6 + X^5 + X3 + 1 49 | #define POCSAG_BCH_GENPOLY 0x00000769U 50 | 51 | CBCH3121::CBCH3121() : m_S1(0U), 52 | m_S3(0U) { 53 | } 54 | 55 | void CBCH3121::encode(uint32_t& data) { 56 | // Just use data part of the codeword 57 | data &= POCSAG_DATA_MASK; 58 | 59 | uint32_t tmp = data; 60 | uint32_t gen_poly = POCSAG_BCH_GENPOLY << POCSAG_BCH_K; 61 | 62 | // Calculate BCH check bits 63 | for (unsigned int i = 0U; i < POCSAG_BCH_K; i++, tmp <<= 1) { 64 | if (tmp & 0x80000000U) 65 | tmp ^= gen_poly; 66 | } 67 | 68 | // Add BCH check bits 69 | data |= (tmp >> POCSAG_BCH_K); 70 | 71 | // Add parity bit 72 | if (calc_parity(data)) 73 | data |= 0x01U; 74 | } 75 | 76 | bool CBCH3121::decode(uint32_t& data, uint16_t& errors) { 77 | int8_t S1_3, tmp, X1, X2; 78 | uint8_t cnt = 0U; 79 | uint8_t Q = 0U; 80 | uint8_t locator[5U]; 81 | uint32_t copy = data; 82 | 83 | // Calculate syndrome 84 | calc_syndrome(data); 85 | 86 | if (m_S1 == -1 && m_S3 == -1) { 87 | // return if no errors 88 | errors += check_parity(data); 89 | return true; 90 | } else { 91 | // Calculate S1^3 in GF(2^5): 92 | S1_3 = (m_S1 * 3) % POCSAG_BCH_N; 93 | 94 | // Check for single error 95 | if (S1_3 == m_S3) { 96 | // Correct single error 97 | data ^= (0x01U << (m_S1 + 1U)); 98 | errors += check_parity(data) + 1U; 99 | return true; 100 | } else { // More than 1 errors 101 | 102 | // Calculate in GF(2^5): 103 | // X1 = S1 104 | // X2 = (S3 + S1^3) / S1 105 | 106 | X1 = m_S1; 107 | 108 | if (m_S3 != -1) 109 | tmp = alpha_to[S1_3] ^ alpha_to[m_S3]; 110 | else 111 | tmp = alpha_to[S1_3]; 112 | 113 | X2 = (index_of[tmp] - m_S1 + POCSAG_BCH_N) % POCSAG_BCH_N; 114 | 115 | // Chien search 116 | for (uint8_t i = 1U; i <= POCSAG_BCH_N; i++) { 117 | Q = 1U; 118 | 119 | if (X1 != -1) { 120 | X1 = (X1 + 1) % POCSAG_BCH_N; 121 | Q ^= alpha_to[X1]; 122 | } 123 | 124 | if (X2 != -1) { 125 | X2 = (X2 + 2) % POCSAG_BCH_N; 126 | Q ^= alpha_to[X2]; 127 | } 128 | 129 | if (!Q) { 130 | locator[cnt] = i % POCSAG_BCH_N; 131 | cnt++; 132 | } 133 | } 134 | 135 | if (cnt != 2U) { // Decoding failed, too many errors 136 | errors += check_parity(data) + 3U; 137 | data = copy; 138 | return false; 139 | } 140 | 141 | // Correct double error: 142 | data ^= (0x80000000U >> (locator[0U] - 1U)); 143 | data ^= (0x80000000U >> (locator[1U] - 1U)); 144 | 145 | errors += check_parity(data) + 2U; 146 | 147 | return true; 148 | } 149 | } 150 | data = copy; 151 | return false; 152 | } 153 | 154 | void CBCH3121::calc_syndrome(uint32_t data) { 155 | // Reset syndromes 156 | m_S1 = 0; 157 | m_S3 = 0; 158 | 159 | // Drop parity bit 160 | data >>= 1; 161 | 162 | // Calculate just syndromes S1 and S3 163 | for (uint8_t i = 0; i < POCSAG_BCH_N; i++, data >>= 1) { 164 | if (data & 0x01U) { 165 | m_S1 ^= alpha_to[i % POCSAG_BCH_N]; 166 | m_S3 ^= alpha_to[(3 * i) % POCSAG_BCH_N]; 167 | } 168 | } 169 | 170 | // Transform polynomial form to index form 171 | m_S1 = index_of[m_S1]; 172 | m_S3 = index_of[m_S3]; 173 | } 174 | 175 | bool CBCH3121::calc_parity(uint32_t data) { 176 | data ^= data >> 16; 177 | data ^= data >> 8; 178 | data ^= data >> 4; 179 | data ^= data >> 2; 180 | data ^= data >> 1; 181 | 182 | return data & 0x01U; 183 | } 184 | 185 | uint16_t CBCH3121::check_parity(uint32_t& data) { 186 | // Check for error in parity bit 187 | if (calc_parity(data)) { 188 | data ^= 0x01; 189 | return 1U; 190 | } else 191 | return 0U; 192 | } -------------------------------------------------------------------------------- /WDS - radio-config/genconfig.php: -------------------------------------------------------------------------------- 1 | #define'", $shortlist); 65 | $properties = array(); 66 | $shortproperties = array(); 67 | foreach($shortlist as $line){ 68 | $line = substr($line,10); 69 | list($property,) = explode(" ",$line,2); 70 | $proplist = array(); 71 | $proplist = explode(",", $line); 72 | $len = count($proplist); 73 | $len = dechex($len); 74 | if(strlen($len)==1)$len = "0".$len; 75 | $len = "0x".$len; 76 | $shortproperties[$property] = $len; 77 | } 78 | //print_r($shortproperties); 79 | } else { 80 | $shortenedconfig = false; 81 | } 82 | 83 | $properties = array(); 84 | foreach ($filelines as $line) { 85 | $line = trim($line); 86 | if (stripos($line, "// RF Freq.(MHz)") !== false) { 87 | $arr = explode(" ", $line); 88 | $freq = $arr[3]; 89 | //echo $freq."\n"; 90 | } 91 | if (stripos($line, "#define RADIO_CONFIGURATION_DATA_ARRAY { \\") !== false) { 92 | $started = true; 93 | } else if ($started) { 94 | $line = trim(trim(trim($line, "\\")), ","); 95 | if (substr($line, 0, 2) == "0x" && substr($line, 0, 4) != "0x00") { 96 | list($len, $property) = explode(",", $line); 97 | $len = trim($len); 98 | $property = trim($property); 99 | if($shortenedconfig){ 100 | $cut = array(); 101 | $cut['RF_POWER_UP'] = 1; // not needed! 102 | $cut['RF_GPIO_PIN_CFG']=1; // not needed! 103 | $cut['RF_GLOBAL_XO_TUNE_2']=1; // not needed! 104 | $cut['RF_GLOBAL_CONFIG_1']=1; // not needed! 105 | 106 | //$cut['RF_INT_CTL_ENABLE_4']=1; // don't cut these 107 | //$cut['RF_FRR_CTL_A_MODE_4']=1; // don't cut these 108 | 109 | //$cut['RF_PKT_FIELD_3_CRC_CONFIG_12']=1; // not needed 110 | $cut['RF_PKT_CRC_SEED_31_24_4']=1; // not needed 111 | //$cut['RF_PA_TC_1']=1; // not needed 112 | //$cut['RF_PKT_RX_FIELD_1_CRC_CONFIG_12']=1; // not needed 113 | //$cut['RF_PKT_RX_FIELD_4_CRC_CONFIG_5']=1; // not needed 114 | //$cut['RF_SYNTH_PFDCP_CPFF_7']=1; // not needed 115 | //$cut['RF_MATCH_VALUE_1_12']=1; // not needed 116 | //$cut['RF_FREQ_CONTROL_INTE_8']=1; // don't cut this! 117 | 118 | //if(isset($cut[$property])){ 119 | //} else { 120 | $properties[$property] = $len; 121 | //} 122 | } else { 123 | $properties[$property] = $len; 124 | } 125 | } 126 | } 127 | if ($started && substr($line, 0, 1) == "}") { 128 | $started = false; 129 | } 130 | } 131 | 132 | //print_r($properties); 133 | // END MAKE LIST OF RELEVANT PROPERTIES 134 | 135 | 136 | $config = chr(count($properties)); 137 | 138 | foreach ($properties as $property => $len) { 139 | foreach ($filelines as $line) { 140 | $line = trim($line); 141 | $search = "#define $property "; 142 | if (substr($line, 0, strlen($search)) == $search) { 143 | $arr = explode(" ", $line, 3); 144 | $bytes = $arr[2]; 145 | $bytes = $len . ", " . $bytes; 146 | $bytes = str_replace("0x", "", $bytes); 147 | $bytes = str_replace(", ", "", $bytes); 148 | $config .= hex2bin($bytes); 149 | } 150 | } 151 | } 152 | 153 | 154 | 155 | $field = bin2hex($config); 156 | $field = chunk_split($field, 2, ", 0x"); 157 | $field = ", 0x" . substr($field, 0, -2); 158 | $hex = substr($field, 2, -2); 159 | if ($rxconfig) { 160 | echo "static const uint8_t PROGMEM rf4463RXsetup$count" . "[] = {" . $hex . "}; // $freq RX\n"; 161 | $rxlist[($freq * 1000000) + $mode] = $count; 162 | } else { 163 | echo "static const uint8_t PROGMEM rf4463TXsetup$count" . "[] = {" . $hex . "}; // $freq TX\n"; 164 | $txlist[($freq * 1000000) + $mode] = $count; 165 | } 166 | $count++; 167 | } 168 | 169 | $farr = "static const uint32_t PROGMEM rxFrequencies[] = {"; 170 | foreach ($rxlist as $entry => $c) { 171 | $farr .= $entry . ", "; 172 | } 173 | $farr = substr($farr, 0, -2); 174 | $farr .= "};\n"; 175 | echo $farr; 176 | 177 | $farr = "static const uint32_t PROGMEM txFrequencies[] = {"; 178 | foreach ($txlist as $entry => $c) { 179 | $farr .= $entry . ", "; 180 | } 181 | $farr = substr($farr, 0, -2); 182 | $farr .= "};\n"; 183 | echo $farr; 184 | 185 | 186 | $carr = " __attribute__ ((unused)) static const uint8_t* PROGMEM configs[] = {"; 187 | foreach ($rxlist as $entry => $c) { 188 | $carr .= "rf4463RXsetup" . $c . ", "; 189 | } 190 | foreach ($txlist as $entry => $c) { 191 | $carr .= "rf4463TXsetup" . $c . ", "; 192 | } 193 | $carr = substr($carr, 0, -2); 194 | $carr .= "};\n"; 195 | echo $carr; 196 | echo "#define RX_FREQS " . count($rxlist) . "\n"; 197 | echo "#define TX_FREQS " . count($txlist) . "\n"; 198 | 199 | -------------------------------------------------------------------------------- /ESP32-firmware/src/pagerkeepalive.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "pagerkeepalive.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "ESPAsyncWebServer.h" 13 | #include "encoder.h" 14 | #include "settings.h" 15 | 16 | std::vector flexidlelist; 17 | #define FLEXIDLEFRAMETIMEOUT 127 18 | #define POCSAGIDLEFRAMETIMEOUT 128 19 | 20 | bool registerFrame(uint32_t freq, uint32_t ric, char prefix) { 21 | //check if the pager is listening on the synced RX frequency, if so, don't register it's frame. 22 | if ((settings.current.mode == MODE_FLEX_SYNCED) && (freq == settings.current.freq)) { 23 | return false; 24 | } 25 | 26 | // check if base frame for this frequency is already in it's idle list 27 | uint8_t frame = flexaddress::getFrame(ric, prefix); 28 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 29 | if ((flexidlelist[c].frame == frame) && (flexidlelist[c].freq == freq)) { 30 | return false; 31 | } 32 | } 33 | 34 | // add to the idle list 35 | struct idlelistentry newentry; 36 | newentry.frame = frame; 37 | newentry.freq = freq; 38 | newentry.timeout = FLEXIDLEFRAMETIMEOUT; 39 | flexidlelist.push_back(newentry); 40 | return true; 41 | } 42 | 43 | bool registerPOCSAG(uint32_t freq, uint16_t baud) { 44 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 45 | if ((flexidlelist[c].frame == baud) && (flexidlelist[c].freq == freq)) { 46 | return false; 47 | } 48 | } 49 | struct idlelistentry newentry; 50 | newentry.frame = baud; 51 | newentry.freq = freq; 52 | newentry.timeout = POCSAGIDLEFRAMETIMEOUT; 53 | flexidlelist.push_back(newentry); 54 | return true; 55 | } 56 | 57 | void IRAM_ATTR resetIdleCounter(uint32_t freq, uint16_t frame) { 58 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 59 | if (flexidlelist[c].freq == freq) { 60 | if (flexstatus.collapse == 0) { 61 | if (flexidlelist[c].frame > 500) { 62 | if (frame == flexidlelist[c].frame) { 63 | flexidlelist[c].timeout = POCSAGIDLEFRAMETIMEOUT; 64 | } 65 | } else { 66 | flexidlelist[c].timeout = FLEXIDLEFRAMETIMEOUT; 67 | } 68 | } else { 69 | if (flexidlelist[c].frame > 500) { 70 | if (frame == flexidlelist[c].frame) { 71 | flexidlelist[c].timeout = POCSAGIDLEFRAMETIMEOUT; 72 | } 73 | } else { 74 | uint8_t match = ((1 << flexstatus.collapse) - 1); 75 | if ((frame & match) == (flexidlelist[c].frame & match)) { 76 | flexidlelist[c].timeout = FLEXIDLEFRAMETIMEOUT; 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | void IRAM_ATTR decrementIdleCounter() { 85 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 86 | if (flexidlelist[c].timeout) flexidlelist[c].timeout--; 87 | } 88 | } 89 | 90 | uint32_t IRAM_ATTR shouldSendIdleFlexFrame(uint8_t frame) { 91 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 92 | if ((flexidlelist[c].timeout == 0) && (flexidlelist[c].frame < 500)) { 93 | uint8_t match = ((1 << flexstatus.collapse) - 1); 94 | if ((frame & match) == (flexidlelist[c].frame & match)) { 95 | return flexidlelist[c].freq; 96 | } 97 | } 98 | } 99 | return 0; 100 | } 101 | 102 | idlelistentry IRAM_ATTR shouldSendIdlePOCSAG() { 103 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 104 | if (flexidlelist[c].timeout == 0) { 105 | if (flexidlelist[c].frame > 500) { 106 | return flexidlelist[c]; 107 | } 108 | } 109 | } 110 | idlelistentry leeg; 111 | leeg.freq = 0; 112 | return leeg; 113 | } 114 | 115 | void getIdleList(AsyncWebServerRequest *request) { 116 | DynamicJsonDocument data(30 + 80 * flexidlelist.size()); 117 | JsonArray pagers = data.createNestedArray("pagers"); 118 | for (uint8_t c = 0; c < flexidlelist.size(); c++) { 119 | JsonObject pager = pagers.createNestedObject(); 120 | pager["frame"] = flexidlelist[c].frame; 121 | pager["freq"] = flexidlelist[c].freq; 122 | pager["timeout"] = flexidlelist[c].timeout; 123 | } 124 | String response; 125 | serializeJson(data, response); 126 | request->send(200, "application/json", response); 127 | } 128 | 129 | void saveIdleList() { 130 | DynamicJsonDocument data(30 + 50 * flexidlelist.size()); 131 | JsonArray pagers = data.createNestedArray("pagers"); 132 | for (uint8_t c = 0; c < flexidlelist.size(); c++) { 133 | JsonObject pager = pagers.createNestedObject(); 134 | pager["frame"] = flexidlelist[c].frame; 135 | pager["freq"] = flexidlelist[c].freq; 136 | } 137 | File file = LittleFS.open("/idlelist.json", "w"); 138 | if (serializeJson(data, file) == 0) { 139 | Serial.println(F("Failed to write to file")); 140 | } 141 | file.close(); 142 | } 143 | 144 | void loadIdleList() { 145 | //StaticJsonDocument<2048> doc; 146 | DynamicJsonDocument doc(1500); 147 | File file = LittleFS.open("/idlelist.json"); 148 | Serial.println("Loading Pager Idle list: "); 149 | while (file.available()) { 150 | Serial.write(file.read()); 151 | } 152 | Serial.println(""); 153 | Serial.println("----------------"); 154 | file.seek(0); 155 | 156 | if (!file) { 157 | ets_printf("Can't read idlelist file, bailing\n"); 158 | return; 159 | } else { 160 | DeserializationError error = deserializeJson(doc, file); 161 | if (error) { 162 | ets_printf("Failed to read json file, idlelist corrupted?\n"); 163 | return; 164 | } 165 | } 166 | file.close(); 167 | JsonArray array = doc["pagers"].as(); 168 | for (JsonVariant v : array) { 169 | idlelistentry pager; 170 | pager.frame = v["frame"].as(); 171 | pager.freq = v["freq"].as(); 172 | pager.timeout = 64; 173 | flexidlelist.push_back(pager); 174 | } 175 | } 176 | 177 | void delFromIdleList(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { 178 | StaticJsonDocument<150> doc; 179 | DeserializationError error = deserializeJson(doc, data); 180 | if (error) { 181 | return; 182 | } else { 183 | uint16_t frameno = doc["frame"]; 184 | uint32_t freq = doc["freq"]; 185 | for (uint16_t c = 0; c < flexidlelist.size(); c++) { 186 | if ((flexidlelist[c].frame == frameno) && (flexidlelist[c].freq == freq)) { 187 | flexidlelist.erase(flexidlelist.begin() + c); 188 | saveIdleList(); 189 | return; 190 | } 191 | } 192 | } 193 | } -------------------------------------------------------------------------------- /ESP32-firmware/src/jwifi.cpp: -------------------------------------------------------------------------------- 1 | #include "jwifi.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ESPAsyncWebServer.h" 10 | #include "httpd.h" 11 | #include "led.h" 12 | #include "settings.h" 13 | #include "statustracker.h" 14 | 15 | #define AP_NAME "PagerModem" 16 | #define AP_PASSWORD "password" 17 | 18 | AsyncDNSServer dnsServer; 19 | extern AsyncWebServer server; 20 | char macStr[13] = {0}; 21 | char shortmac[6] = {0}; 22 | enum wifistatusenum wifiStatus; 23 | enum wifistatusenum prevWifiStatus; 24 | #define RECONNECT_INTERVAL 10 25 | uint8_t reconnectCountdown = RECONNECT_INTERVAL; 26 | uint16_t reconnectAttempts = 0; 27 | volatile bool wifiConnected = false; 28 | 29 | void wifiConfigModeCallback(ESPAsync_WiFiManager *myWiFiManager); 30 | void WiFiEvent(WiFiEvent_t event) { 31 | switch (event) { 32 | case SYSTEM_EVENT_STA_START: 33 | case SYSTEM_EVENT_WIFI_READY: 34 | wifiStatus = wifistatusenum::READY; 35 | break; 36 | case SYSTEM_EVENT_STA_CONNECTED: 37 | wifiStatus = wifistatusenum::CONNECTED; 38 | break; 39 | case SYSTEM_EVENT_STA_DISCONNECTED: 40 | case SYSTEM_EVENT_STA_STOP: 41 | case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: 42 | case SYSTEM_EVENT_STA_LOST_IP: 43 | wifiStatus = wifistatusenum::DISCONNECTED; 44 | break; 45 | case SYSTEM_EVENT_STA_GOT_IP: 46 | wifiStatus = wifistatusenum::GOT_IP; 47 | break; 48 | default: 49 | Serial.printf("[WiFi-event] event: %d\n", event); 50 | break; 51 | } 52 | } 53 | 54 | void WiFiEventPre(WiFiEvent_t event) { 55 | if (event == 5) { 56 | ets_printf("Reconnecting from WiFiEvent\n"); 57 | WiFi.reconnect(); 58 | } 59 | } 60 | 61 | void wifiProcess(void *parameter) { 62 | uint8_t mac[6]; 63 | esp_efuse_mac_get_default(mac); 64 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_DISCONNECTED, eSetBits); 65 | Serial.println("Launching WiFi Process..."); 66 | sprintf(macStr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 67 | sprintf(shortmac, "%02X%02X", mac[4], mac[5]); 68 | Serial.print(F("MAC: ")); 69 | Serial.println(macStr); 70 | wifiInit(); 71 | WiFi.onEvent(WiFiEvent); 72 | 73 | if ((WiFi.status() == WL_CONNECTED) && 74 | WiFi.localIP() && 75 | WiFi.isConnected()) { 76 | wifiStatus = wifistatusenum::GOT_IP; 77 | ets_printf("Connected to WiFi - IP: "); 78 | sendStatus(STATUS_NEWIP); 79 | Serial.println(WiFi.localIP()); 80 | wifiConnected = true; 81 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_CONNECTED, eSetBits); 82 | } else { 83 | wifiStatus = wifistatusenum::DISCONNECTED; 84 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_DISCONNECTED, eSetBits); 85 | } 86 | prevWifiStatus = wifiStatus; 87 | sendStatus(STATUS_UPDATERSSI); 88 | uint8_t doRSSI = 0; 89 | while (true) { 90 | if (prevWifiStatus != wifiStatus) { // status change 91 | if (wifiStatus == wifistatusenum::GOT_IP) { 92 | ets_printf("Connected to WiFi - IP: "); 93 | Serial.println(WiFi.localIP()); 94 | wifiConnected = true; 95 | HTTPrestartNeeded = true; 96 | reconnectAttempts = 0; 97 | sendStatus(STATUS_NEWIP); 98 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_CONNECTED, eSetBits); 99 | } else { 100 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_DISCONNECTED, eSetBits); 101 | ets_printf("Disconnected!!!\n"); 102 | wifiConnected = false; 103 | } 104 | prevWifiStatus = wifiStatus; 105 | } 106 | if (wifiStatus != wifistatusenum::GOT_IP) { 107 | // not online; 108 | reconnectCountdown--; 109 | if (!reconnectCountdown) { 110 | reconnectCountdown = RECONNECT_INTERVAL; 111 | reconnectAttempts++; 112 | if (reconnectAttempts == 180) { 113 | wifiReset(); 114 | } 115 | if (reconnectAttempts >= 360) { 116 | ESP.restart(); 117 | } 118 | ets_printf("Reconnecting...\n"); 119 | WiFi.reconnect(); 120 | } 121 | } 122 | vTaskDelay(1000 / portTICK_PERIOD_MS); 123 | if (doRSSI == 10) { 124 | doRSSI = 0; 125 | sendStatus(STATUS_UPDATERSSI); 126 | } else { 127 | doRSSI++; 128 | } 129 | } 130 | } 131 | 132 | void wifiResetConfig() { 133 | //DNSServer dnsServer; 134 | Serial.println("Removing settings and resetting.."); 135 | vTaskDelay(50 / portTICK_PERIOD_MS); 136 | WiFi.disconnect(true, true); 137 | vTaskDelay(200 / portTICK_PERIOD_MS); 138 | ESP.restart(); 139 | } 140 | 141 | void wifiInit() { 142 | AsyncDNSServer dnsServer; 143 | String ssid = AP_NAME + String(" ") + String(shortmac); 144 | ESPAsync_WiFiManager wifiManager(&server, &dnsServer, (const char *)ssid.c_str()); 145 | WiFi.onEvent(WiFiEventPre); 146 | WiFi.setHostname((const char *)ssid.c_str()); 147 | wifiManager.setConnectTimeout(10); 148 | wifiManager.setDebugOutput(false); 149 | wifiManager.setBreakAfterConfig(true); 150 | wifiManager.setTimeout(180); 151 | //wifiManager.setAPCallback(wifiConfigModeCallback); 152 | //wifiManager.setSaveConfigCallback(wifiSaveConfigCallback); 153 | ssid = AP_NAME + String(" Setup ") + String(shortmac); 154 | if (!wifiManager.autoConnect((const char *)ssid.c_str(), AP_PASSWORD)) { 155 | // set LED to red 156 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_DISCONNECTED, eSetBits); 157 | Serial.println("failed to connect, reset and try again.."); 158 | delay(500); 159 | wifiReset(); 160 | ESP.restart(); 161 | }; 162 | esp_wifi_set_mode(WIFI_MODE_STA); 163 | } 164 | 165 | void wifiConfigModeCallback(ESPAsync_WiFiManager *myWiFiManager) { 166 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_PORTAL, eSetBits); 167 | Serial.print("Entered config mode: "); 168 | Serial.print(WiFi.softAPIP()); 169 | Serial.print(" on "); 170 | Serial.println(myWiFiManager->getConfigPortalSSID()); 171 | } 172 | 173 | void wifiSaveConfigCallback() { 174 | Serial.println(F("Saved config")); 175 | } 176 | 177 | void wifiReset() { 178 | esp_wifi_disconnect(); 179 | vTaskDelay(50 / portTICK_PERIOD_MS); 180 | esp_wifi_set_mode(WIFI_MODE_NULL); 181 | vTaskDelay(50 / portTICK_PERIOD_MS); 182 | esp_wifi_set_mode(WIFI_MODE_STA); 183 | vTaskDelay(200 / portTICK_PERIOD_MS); 184 | wifi_country_t country = { 185 | .cc = "JP", 186 | .schan = 1, 187 | .nchan = 14, 188 | .max_tx_power = 126, 189 | .policy = WIFI_COUNTRY_POLICY_AUTO, 190 | }; 191 | esp_wifi_set_country(&country); 192 | } -------------------------------------------------------------------------------- /ESP32-firmware/src/statustracker.cpp: -------------------------------------------------------------------------------- 1 | #include "statustracker.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ESPAsyncWebServer.h" 11 | #include "RF4463.h" 12 | #include "decoder.h" 13 | #include "encoder.h" 14 | #include "httpd.h" 15 | #include "jwifi.h" 16 | #include "pagerkeepalive.h" 17 | #include "settings.h" 18 | 19 | #define UPDATEQUEUESIZE 15 20 | QueueHandle_t updatequeue; 21 | struct statusstruct status; 22 | 23 | void statustrackerTask(void* parameter) { 24 | updatequeue = xQueueCreate(UPDATEQUEUESIZE, sizeof(uint32_t)); 25 | bool signal = false; 26 | bool rx = false; 27 | bool tx = false; 28 | bool flexsyncchannel = false; 29 | bool flexsyncntp = false; 30 | uint32_t lastseenfreq; 31 | uint32_t update; 32 | status.currentmode = 0; 33 | while (true) { 34 | portYIELD(); 35 | BaseType_t updateq = xQueueReceive(updatequeue, &update, portMAX_DELAY); 36 | if (updateq == pdTRUE) { 37 | switch (update) { 38 | case STATUS_WIFI_ACTIVITY: 39 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_WIFI_ACTIVITY, eSetBits); 40 | break; 41 | case STATUS_FRAMELIST_UPDATE: 42 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_SYNC_ACTIVITY, eSetBits); 43 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_FRAME_STATUS, eSetBits); 44 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_FRAMECHANGE, eSetBits); 45 | break; 46 | case STATUS_TX: { 47 | if (rx) { 48 | rx = false; 49 | status.rxSignal = false; 50 | status.rxActive = false; 51 | signal = false; 52 | } 53 | if (!tx) { 54 | tx = true; 55 | signal = false; 56 | status.txActive = true; 57 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_RF_TX, eSetBits); 58 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_MODE_STATUS, eSetBits); 59 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_MODE_TX, eSetBits); 60 | } 61 | if (status.freq != lastseenfreq) { 62 | lastseenfreq = status.freq; 63 | //if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_MODE_STATUS, eSetBits); 64 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_FREQCHANGE, eSetBits); 65 | } 66 | } break; 67 | case STATUS_RX_NO_SIGNAL: { 68 | if (tx) { 69 | tx = false; 70 | status.txActive = false; 71 | } 72 | if (!rx) { 73 | rx = true; 74 | status.rxActive = true; 75 | status.rxSignal = false; 76 | signal = false; 77 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_RF_RX_NOSIGNAL, eSetBits); 78 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_MODE_STATUS, eSetBits); 79 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_MODE_RX, eSetBits); 80 | } 81 | if (status.freq != lastseenfreq) { 82 | lastseenfreq = status.freq; 83 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_FREQCHANGE, eSetBits); 84 | //if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_MODE_STATUS, eSetBits); 85 | } 86 | if (signal) { 87 | signal = false; 88 | status.rxSignal = false; 89 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_RF_RX_NOSIGNAL, eSetBits); 90 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_SIGNAL, eSetBits); 91 | } 92 | } break; 93 | case STATUS_RX_SIGNAL: 94 | if (!signal) { 95 | signal = true; 96 | status.rxSignal = true; 97 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_RF_RX_SIGNAL, eSetBits); 98 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_SIGNAL | WS_SEND_FRAME_STATUS, eSetBits); 99 | } 100 | break; 101 | case STATUS_NEW_WS_CONNECTION: 102 | if (status.websocketUpdater) xTaskNotify(status.websocketUpdater, WS_SEND_FRAME_STATUS | WS_SEND_MODE_STATUS | WS_SEND_MODMODE | WS_SEND_SIGNAL, eSetBits); 103 | break; 104 | case STATUS_SYNCED_FLEX: 105 | if (!flexsyncchannel) { 106 | flexsyncchannel = true; 107 | flexsyncntp = false; 108 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_SYNC_SYNCED_TO_CHANNEL, eSetBits); 109 | } 110 | break; 111 | case STATUS_SYNCED_GPS: 112 | if (!flexsyncntp) { 113 | flexsyncntp = true; 114 | flexsyncchannel = false; 115 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_SYNC_SYNCED_NTP, eSetBits); 116 | } 117 | case STATUS_SYNCED_NOSYNC: 118 | if (flexsyncchannel || flexsyncntp) { 119 | flexsyncchannel = false; 120 | flexsyncntp = false; 121 | if (status.ledupdater) xTaskNotify(status.ledupdater, LED_SYNC_NOSYNC, eSetBits); 122 | } 123 | case STATUS_MODE_FLEX1600: 124 | case STATUS_MODE_FLEX3200_2: 125 | case STATUS_MODE_FLEX3200_4: 126 | case STATUS_MODE_FLEX6400_4: 127 | case STATUS_MODE_POCSAG512: 128 | case STATUS_MODE_POCSAG1200: 129 | case STATUS_MODE_POCSAG2400: 130 | if (update != status.currentmode) { 131 | status.currentmode = update; 132 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_MODECHANGE, eSetBits); 133 | } 134 | break; 135 | case STATUS_NEWIP: 136 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_IPCHANGE, eSetBits); 137 | break; 138 | case STATUS_UPDATERSSI: 139 | if (status.lcdupdater) xTaskNotify(status.lcdupdater, LCD_UPDATERSSI, eSetBits); 140 | break; 141 | default: 142 | ets_printf("Notification: %u\n", update); 143 | break; 144 | } 145 | } 146 | } 147 | } 148 | 149 | void sendStatus(uint32_t statusval) { 150 | xQueueSend(updatequeue, &statusval, 0); 151 | } 152 | 153 | void IRAM_ATTR sendStatusISR(uint32_t statusval) { 154 | xQueueSendFromISR(updatequeue, &statusval, NULL); 155 | } -------------------------------------------------------------------------------- /ESP32-firmware/src/lcd.cpp: -------------------------------------------------------------------------------- 1 | #define FS_NO_GLOBALS 2 | #include "lcd.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "RF4463.h" 10 | #include "encoder.h" 11 | #include "fonts.h" 12 | #include "images.h" 13 | #include "settings.h" 14 | #include "statustracker.h" 15 | 16 | TFT_eSPI tft = TFT_eSPI(135, 240); 17 | 18 | uint32_t lcd_lastric = 0; 19 | uint32_t lcd_lastric_drawn = 0; 20 | uint8_t lcd_lastfunc; 21 | bool lcd_msg_changed = true; 22 | 23 | void lcdSetup() { 24 | tft.init(); 25 | tft.setRotation(1); 26 | tft.fillScreen(TFT_BLACK); 27 | tft.setSwapBytes(true); 28 | tft.pushImage(0, 0, 240, 135, bg); 29 | 30 | tft.loadFont(lcars24); // Load another different font 31 | tft.setTextDatum(BL_DATUM); 32 | 33 | //blauw 34 | tft.setTextColor(0x3A38, TFT_BLACK); // Change the font colour and the background colour 35 | tft.fillRect(40, 20, 128, 22, 0x0000); 36 | tft.drawString("460.91875 MHz", 40, 47); 37 | 38 | tft.fillRect(186, 20, 55, 20, 0x0000); 39 | tft.drawString("RX", 190, 47); 40 | 41 | tft.drawLine(17, 44, 229, 44, 0x3A38); 42 | 43 | // oranje block 44 | tft.fillRect(40, 47, 107, 20, 0x0000); 45 | tft.setTextColor(0xFBE4, TFT_BLACK); // Change the font colour and the background colour 46 | tft.drawString("POCSAG1200", 40, 72); 47 | 48 | tft.fillRect(190, 47, 50, 20, 0x0000); 49 | tft.setTextColor(0xFBE4, TFT_BLACK); // Change the font colour and the background colour 50 | tft.drawString("127", 190, 72); 51 | 52 | tft.drawLine(17, 69, 222, 69, 0xFBE4); 53 | tft.unloadFont(); 54 | 55 | // paars block 56 | tft.setTextColor(0xA254); 57 | tft.setCursor(40, 77); 58 | tft.fillRect(36, 71, 92, 15, 0x0000); 59 | //tft.print(WiFi.localIP()); 60 | 61 | tft.setTextColor(0xA254); 62 | tft.fillRect(190, 71, 49, 15, 0x0000); 63 | tft.setCursor(190, 77); 64 | //tft.print(WiFi.RSSI()); 65 | //tft.print(" dBm"); 66 | 67 | tft.drawLine(17, 86, 229, 86, 0xA254); 68 | } 69 | 70 | void updateState() { 71 | char buffer[16]; 72 | /* 73 | if (flex_rx_state > 1) { 74 | tft.pushImage(55, 72, 50, 10, icon_synced); 75 | } else { 76 | tft.pushImage(55, 72, 50, 10, icon_nosignal); 77 | tft.fillRect(106, 72, 103, 10, 0x0000); 78 | } 79 | if (flex_rx_state == 10) { 80 | tft.pushImage(106, 72, 50, 10, icon_message); 81 | } 82 | if (flex_rx_state > 10) { 83 | tft.pushImage(158, 72, 50, 10, icon_idle); 84 | } 85 | */ 86 | } 87 | 88 | uint32_t screenfreq; 89 | bool fontLoaded = false; 90 | 91 | void lcdTask(void *parameter) { 92 | lcdSetup(); 93 | status.lcdupdater = xTaskGetCurrentTaskHandle(); 94 | uint32_t action = 0; 95 | while (1) { 96 | action = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 97 | if (action == 0) { 98 | // timeout 99 | } else { 100 | if (action & LCD_FREQCHANGE) { 101 | if (screenfreq != status.freq) { 102 | screenfreq = status.freq; 103 | double freq = (double)status.freq / 1000000; 104 | if (!fontLoaded) { 105 | tft.loadFont(lcars24); 106 | fontLoaded = true; 107 | } 108 | tft.setTextColor(0x3A38, TFT_BLACK); 109 | tft.fillRect(40, 20, 128, 22, 0x0000); 110 | tft.setCursor(40, 24); 111 | tft.printf("%.8g MHz", freq); 112 | } 113 | } 114 | if (action & LCD_MODE_RX) { 115 | if (!fontLoaded) { 116 | tft.loadFont(lcars24); 117 | fontLoaded = true; 118 | } 119 | tft.setTextColor(0x3A38, TFT_BLACK); 120 | tft.fillRect(190, 20, 50, 22, 0x0000); 121 | tft.setCursor(190, 24); 122 | tft.print("RX"); 123 | } 124 | if (action & LCD_MODE_TX) { 125 | if (!fontLoaded) { 126 | tft.loadFont(lcars24); 127 | fontLoaded = true; 128 | } 129 | tft.setTextColor(0x3A38, TFT_BLACK); 130 | tft.fillRect(190, 20, 49, 22, 0x0000); 131 | tft.setCursor(190, 24); 132 | tft.print("TX"); 133 | } 134 | if (action & LCD_MODECHANGE) { 135 | if (!fontLoaded) { 136 | tft.loadFont(lcars24); 137 | fontLoaded = true; 138 | } 139 | tft.fillRect(40, 47, 107, 20, 0x0000); 140 | tft.setTextColor(0xFBE4, TFT_BLACK); // Change the font colour and the background colour 141 | tft.setCursor(40, 49); 142 | switch (status.currentmode) { 143 | case STATUS_MODE_FLEX1600: 144 | tft.print("FLEX 1600"); 145 | break; 146 | case STATUS_MODE_FLEX3200_2: 147 | tft.print("FLEX 3200 2"); 148 | break; 149 | case STATUS_MODE_FLEX3200_4: 150 | tft.print("FLEX 3200 4"); 151 | break; 152 | case STATUS_MODE_FLEX6400_4: 153 | tft.print("FLEX 6400 4"); 154 | break; 155 | case STATUS_MODE_POCSAG512: 156 | tft.print("POCSAG 512"); 157 | break; 158 | case STATUS_MODE_POCSAG1200: 159 | tft.print("POCSAG 1200"); 160 | break; 161 | case STATUS_MODE_POCSAG2400: 162 | tft.print("POCSAG 2400"); 163 | break; 164 | } 165 | } 166 | 167 | if (action & LCD_FRAMECHANGE) { 168 | if (!fontLoaded) { 169 | tft.loadFont(lcars24); 170 | fontLoaded = true; 171 | } 172 | tft.fillRect(190, 47, 50, 20, 0x0000); 173 | tft.setTextColor(0xFBE4, TFT_BLACK); // Change the font colour and the background colour 174 | tft.setCursor(190, 49); 175 | tft.print(flexstatus.frame); 176 | } 177 | 178 | if (action & LCD_MODE_IDLE) { 179 | if (!fontLoaded) { 180 | tft.loadFont(lcars24); 181 | fontLoaded = true; 182 | } 183 | tft.setTextColor(0x3A38, TFT_BLACK); 184 | tft.fillRect(190, 20, 50, 20, 0x0000); 185 | tft.setCursor(190, 24); 186 | tft.print("IDLE"); 187 | } 188 | 189 | if (fontLoaded) { 190 | tft.unloadFont(); 191 | fontLoaded = false; 192 | } 193 | 194 | if (action & LCD_IPCHANGE) { 195 | tft.setTextColor(0xA254); 196 | tft.setCursor(40, 76); 197 | tft.fillRect(36, 71, 92, 14, 0x0000); 198 | tft.print(WiFi.localIP()); 199 | } 200 | if (action & LCD_UPDATERSSI) { 201 | if (WiFi.isConnected()) { 202 | tft.setTextColor(0xA254); 203 | tft.fillRect(190, 71, 49, 14, 0x0000); 204 | tft.setCursor(190, 76); 205 | tft.print(WiFi.RSSI()); 206 | tft.print(" dBm"); 207 | } 208 | } 209 | } 210 | } 211 | } -------------------------------------------------------------------------------- /WDS - radio-config/pocsag_2400_tx.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 8 | 9 | 10 | 460.91875 11 | 12 | 13 | 25 14 | 15 | 16 | 17 | 18 | False 19 | 20 | 21 | 10 22 | 23 | 24 | 10 25 | 26 | 27 | 30 28 | 29 | 30 | 82 31 | 32 | 33 | 34 | False 35 | 36 | 37 | 38 | 1 39 | 40 | 41 | 42 | 43 | False 44 | 45 | 46 | 29 47 | 48 | 49 | 0 50 | 51 | 52 | 127 53 | 54 | 55 | 56 | 57 | 58 | 59 | 2 60 | 61 | 62 | 38.400 63 | 64 | 65 | 4.5 66 | 67 | 68 | 69 | 70 | 71 | 72 | False 73 | 74 | 75 | False 76 | 0 77 | 78 | 79 | 80 | 81 | False 82 | 83 | 84 | False 85 | 0 86 | 87 | 88 | 89 | 90 | True 91 | 92 | 93 | True 94 | 64 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 0 103 | 104 | 105 | 0 106 | 107 | 108 | 0 109 | 110 | 111 | 0 112 | 113 | 114 | 115 | 116 | False 117 | 118 | 119 | INPUT 120 | 121 | 122 | False 123 | 124 | 125 | TX_DATA_CLK 126 | 127 | 128 | False 129 | 130 | 131 | DONOTHING 132 | 133 | 134 | False 135 | 136 | 137 | DONOTHING 138 | 139 | 140 | False 141 | 142 | 143 | DONOTHING 144 | 145 | 146 | False 147 | 148 | 149 | DONOTHING 150 | 151 | 152 | 0 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /ESP32-firmware/src/settings.cpp: -------------------------------------------------------------------------------- 1 | #include "settings.h" 2 | 3 | #include 4 | #include 5 | //#include 6 | #include 7 | //#include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "ESPAsyncWebServer.h" 14 | #include "RF4463.h" 15 | #include "decoder.h" 16 | #include "statustracker.h" 17 | 18 | extern AsyncWebSocket ws; 19 | 20 | struct settingsstruct settings; 21 | 22 | void setupSettings() { 23 | if (!LittleFS.begin()) { 24 | if (!LittleFS.format()) { 25 | ets_printf("Error occurred while mounting FS\n"); 26 | ets_printf("Error formatting FS!!!\n"); 27 | } else { 28 | ets_printf("Filesystem created "); 29 | if (!LittleFS.begin()) { 30 | ets_printf("but failed to mount :(\n"); 31 | } else { 32 | ets_printf("and mounted\n"); 33 | } 34 | } 35 | } else { 36 | ets_printf("Filesystem mounted, %u/%u bytes used\n", LittleFS.usedBytes(), LittleFS.totalBytes()); 37 | } 38 | loadJSONFile(); 39 | } 40 | 41 | void getSettings(AsyncWebServerRequest *request) { 42 | ets_printf("getsettings called\n"); 43 | if (settings.webportal.loginRequired) { 44 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 45 | return request->requestAuthentication(); 46 | } 47 | } 48 | request->send(LittleFS, "/settings.json", "application/json"); 49 | } 50 | 51 | void saveSettingsComplete(AsyncWebServerRequest *request) { 52 | if (settings.webportal.loginRequired) { 53 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 54 | return; 55 | } 56 | } 57 | if (!loadJSONFile()) { 58 | request->send(200, "application/json", "{\"status\":\"CONFIG_INVALID\"}"); 59 | } else { 60 | request->send(200, "application/json", "{\"status\":\"CONFIG_VALID\"}"); 61 | } 62 | } 63 | 64 | void saveSettings(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { 65 | if (settings.webportal.loginRequired) { 66 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 67 | return request->requestAuthentication(); 68 | } 69 | } 70 | ets_printf("Save New Settings\n"); 71 | File file = LittleFS.open("/settings.json", "w"); 72 | file.write(data, len); 73 | file.close(); 74 | } 75 | void uploadOTACertComplete(AsyncWebServerRequest *request) { 76 | if (settings.webportal.loginRequired) { 77 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 78 | return; 79 | } 80 | } 81 | request->send(200, "application/json", "{\"status\":\"OTACERT_SAVED\"}"); 82 | } 83 | void uploadOTACert(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 84 | if (settings.webportal.loginRequired) { 85 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 86 | return request->requestAuthentication(); 87 | } 88 | } 89 | if (!index) { 90 | LittleFS.remove("/otacert.pem"); 91 | } 92 | File file = LittleFS.open("/otacert.pem", "a"); 93 | file.write(data, len); 94 | file.close(); 95 | } 96 | void getOTACert(AsyncWebServerRequest *request) { 97 | ets_printf("getotacert called\n"); 98 | if (settings.webportal.loginRequired) { 99 | if (!request->authenticate(settings.webportal.username, settings.webportal.password)) { 100 | return request->requestAuthentication(); 101 | } 102 | } 103 | request->send(LittleFS, "/otacert.pem", "application/json"); // todo, right content type 104 | } 105 | void getMode(AsyncWebServerRequest *request) { 106 | StaticJsonDocument<100> data; 107 | switch (settings.current.mode) { 108 | case 0: 109 | data["mode"] = "MODE_FLEX"; 110 | break; 111 | case 1: 112 | data["mode"] = "MODE_POCSAG"; 113 | break; 114 | case 2: 115 | data["mode"] = "MODE_TX_ONLY"; 116 | break; 117 | case 3: 118 | data["mode"] = "MODE_FLEX_SYNCED"; 119 | break; 120 | } 121 | data["freq"] = settings.current.freq; 122 | data["baud"] = settings.current.baud; 123 | String response; 124 | serializeJson(data, response); 125 | request->send(200, "application/json", response); 126 | } 127 | void setMode(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { 128 | StaticJsonDocument<512> doc; 129 | bool configValid = false; 130 | DeserializationError error = deserializeJson(doc, data); 131 | if (error) { 132 | ets_printf("Failed to read json file, config corrupted? Using defaults\n"); 133 | } else { 134 | bool freqValid = true; 135 | 136 | if (doc["mode"] == "MODE_FLEX") { 137 | freqValid = RF4463::checkAvail(doc["freq"], RF4463::FLEX_1600_RX); 138 | } else if (doc["mode"] == "MODE_POCSAG") { 139 | uint16_t tempbaud = doc["baud"] | 1200; 140 | switch (tempbaud) { 141 | case 512: 142 | freqValid = RF4463::checkAvail(doc["freq"], RF4463::POCSAG_512_RX); 143 | break; 144 | case 1200: 145 | freqValid = RF4463::checkAvail(doc["freq"], RF4463::POCSAG_1200_RX); 146 | break; 147 | case 2400: 148 | freqValid = RF4463::checkAvail(doc["freq"], RF4463::POCSAG_2400_RX); 149 | break; 150 | } 151 | } else if (doc["mode"] == "MODE_FLEX_SYNCED") { 152 | freqValid = RF4463::checkAvail(doc["freq"], RF4463::FLEX_1600_RX); 153 | } 154 | 155 | if (freqValid) { 156 | settings.current.baud = doc["baud"] | 1200; 157 | if (doc["mode"] == "MODE_FLEX") { 158 | settings.current.mode = MODE_FLEX; 159 | } else if (doc["mode"] == "MODE_POCSAG") { 160 | settings.current.mode = MODE_POCSAG; 161 | } else if (doc["mode"] == "MODE_TX_ONLY") { 162 | settings.current.mode = MODE_TX_ONLY; 163 | } else if (doc["mode"] == "MODE_FLEX_SYNCED") { 164 | settings.current.mode = MODE_FLEX_SYNCED; 165 | } 166 | settings.current.freq = doc["freq"]; 167 | resetRX = true; 168 | } 169 | 170 | if (status.websocketUpdater) xTaskNotifyFromISR(status.websocketUpdater, 0x04, eSetBits, NULL); 171 | } 172 | } 173 | void saveJSONFile() { 174 | File file = LittleFS.open("/settings.json", "w"); 175 | StaticJsonDocument<2048> doc; 176 | JsonObject gps = doc.createNestedObject("gps"); 177 | gps["ppsenabled"] = settings.gps.ppsenabled; 178 | 179 | JsonObject webportal = doc.createNestedObject("webportal"); 180 | webportal["loginrequired"] = settings.webportal.loginRequired; 181 | webportal["username"] = settings.webportal.username; 182 | webportal["password"] = settings.webportal.password; 183 | webportal["ntpserver"] = settings.webportal.ntpserver; 184 | 185 | JsonObject mqtt = doc.createNestedObject("mqtt"); 186 | mqtt["enabled"] = settings.mqtt.enabled; 187 | mqtt["host"] = settings.mqtt.host; 188 | mqtt["port"] = settings.mqtt.port; 189 | mqtt["username"] = settings.mqtt.username; 190 | mqtt["password"] = settings.mqtt.password; 191 | 192 | JsonObject rf = doc.createNestedObject("rf"); 193 | rf["defaultmode"] = settings.rf.defaultmode; 194 | rf["defaultfrequency"] = settings.rf.defaultfrequency; 195 | rf["defaultoffset"] = settings.rf.defaultoffset; 196 | rf["defaultxmitpower"] = settings.rf.defaultxmitpower; 197 | rf["defaultbaud"] = settings.rf.defaultbaud; 198 | 199 | JsonObject pocsag = doc.createNestedObject("pocsag"); 200 | pocsag["typebymessagecontent"] = settings.pocsag.typebymessagecontent; 201 | pocsag["markerrors"] = settings.pocsag.markerrors; 202 | 203 | JsonObject flex = doc.createNestedObject("flex"); 204 | flex["autosendidleframes"] = settings.flex.autosendidleframes; 205 | flex["allowSendToRX"] = settings.flex.allowSendToRX; 206 | flex["collapse"] = settings.flex.collapse; 207 | 208 | JsonObject indi = doc.createNestedObject("indicators"); 209 | indi["lcdEnabled"] = settings.indicators.lcdenabled; 210 | indi["ledsBrightness"] = settings.indicators.ledsbrightness; 211 | 212 | JsonObject update = doc.createNestedObject("update"); 213 | update["url"] = settings.update.url; 214 | update["usessl"] = settings.update.useSSL; 215 | 216 | if (serializeJson(doc, file) == 0) { 217 | Serial.println(F("Failed to write to file")); 218 | } 219 | file.close(); 220 | } 221 | bool loadJSONFile() { 222 | StaticJsonDocument<2048> doc; 223 | bool configValid = false; 224 | File file = LittleFS.open("/settings.json"); 225 | Serial.println("Loading config: "); 226 | while (file.available()) { 227 | Serial.write(file.read()); 228 | } 229 | Serial.println(""); 230 | Serial.println("----------------"); 231 | file.seek(0); 232 | 233 | if (!file) { 234 | ets_printf("Can't read config file, we'll be using defaults\n"); 235 | } else { 236 | DeserializationError error = deserializeJson(doc, file); 237 | if (error) { 238 | ets_printf("Failed to read json file, config corrupted? Using defaults\n"); 239 | } else { 240 | configValid = true; 241 | } 242 | } 243 | file.close(); 244 | settings.gps.ppsenabled = doc["gps"]["ppsenabled"] | false; 245 | 246 | settings.webportal.loginRequired = doc["webportal"]["loginrequired"] | false; 247 | strlcpy(settings.webportal.username, doc["webportal"]["username"] | "", sizeof(settings.webportal.username)); 248 | strlcpy(settings.webportal.password, doc["webportal"]["password"] | "", sizeof(settings.webportal.password)); 249 | strlcpy(settings.webportal.ntpserver, doc["webportal"]["ntpserver"] | "", sizeof(settings.webportal.ntpserver)); 250 | 251 | settings.mqtt.enabled = doc["mqtt"]["enabled"] | false; 252 | strlcpy(settings.mqtt.host, doc["mqtt"]["host"] | "", sizeof(settings.mqtt.host)); 253 | settings.mqtt.port = doc["mqtt"]["port"] | 1883; 254 | strlcpy(settings.mqtt.username, doc["mqtt"]["username"] | "", sizeof(settings.mqtt.username)); 255 | strlcpy(settings.mqtt.password, doc["mqtt"]["password"] | "", sizeof(settings.mqtt.password)); 256 | 257 | settings.rf.defaultmode = doc["rf"]["defaultmode"] | 0; 258 | settings.rf.defaultfrequency = doc["rf"]["defaultfrequency"] | 169650000; 259 | settings.rf.defaultoffset = doc["rf"]["defaultoffset"] | 87; 260 | settings.rf.defaultxmitpower = doc["rf"]["defaultxmitpower"] | 1; 261 | settings.rf.defaultbaud = doc["rf"]["defaultbaud"] | 1200; 262 | 263 | settings.pocsag.typebymessagecontent = doc["pocsag"]["typebymessagecontent"] | true; 264 | settings.pocsag.markerrors = doc["pocsag"]["markerrors"] | false; 265 | 266 | settings.flex.autosendidleframes = doc["flex"]["autosendidleframes"] | false; 267 | settings.flex.allowSendToRX = doc["flex"]["allowSendToRX"] | false; 268 | settings.flex.collapse = doc["flex"]["collapse"] | 4; 269 | 270 | settings.indicators.lcdenabled = doc["indicators"]["lcdEnabled"] | true; 271 | settings.indicators.ledsbrightness = doc["indicators"]["ledsBrightness"] | 10; 272 | 273 | strlcpy(settings.update.url, doc["update"]["url"] | "OTAHOST", sizeof(settings.update.url)); 274 | settings.update.useSSL = doc["update"]["usessl"]; 275 | 276 | if (!configValid) { 277 | ets_printf("Saving new file\n"); 278 | saveJSONFile(); 279 | return false; 280 | } 281 | return true; 282 | } 283 | -------------------------------------------------------------------------------- /ESP32-firmware/include/flexutils.h: -------------------------------------------------------------------------------- 1 | #ifndef flexutils 2 | #define flexutils 3 | 4 | #include 5 | #include 6 | #include "RF4463.h" 7 | 8 | #define FLEX_A1 0b01111000111100110101100100111001 9 | #define FLEX_A2 0b10000100111001110101100100111001 10 | #define FLEX_A3 0b01001111100101110101100100111001 11 | #define FLEX_A4 0b00100001010111110101100100111001 12 | #define FLEX_AR 0b11001011001000000101100100111001 13 | #define FLEX_INV 0b11111111111111111111111111111111 14 | 15 | struct flexdatablock; 16 | class flexframe; 17 | class flexmsg; 18 | 19 | /* 20 | FLEX pager protocol implementation 21 | 22 | Structure: 23 | A flex frame consists of one or more 'phases'. These phases contain the message and instruction data. Within the 'phase' class, the 24 | information is stored in 'blocks' before being structured in phase words for transmission. This is done to allow for phase/frame 25 | optimization before transmission 26 | 27 | A message or instruction is created in a flexmsg object, and 'getBlock' is called to retrieve the block of phase data. This block is 28 | then assigned to a specific phase. 29 | 30 | ┌──────────────┐ ┌────────────┐ 31 | │Message │ │FLEX Address│ 32 | │-type(instr) │ │ │ 33 | │-Alphadata* │◄──┤-RIC │ 34 | │-Numericdata* │ │-Phase │ 35 | │-FLEX addr*[] │ │-Frame │ 36 | └──────┬───────┘ └────────────┘ 37 | │ 38 | ▼ 39 | ┌──────────┐ ┌───────────────┐ ┌──────────────┐ 40 | │FLEX Frame│ │FLEX Phase │ │Datablocks │ 41 | ├──────────┤ ├───────────────┤ ├──────────────┤ 42 | │-FIW │ │-Data blocks*[]│◄───┤-FLEX addr*[] │ 43 | │-Phases[] │◄───┤-BIW One │ │-Data words * │ 44 | └──────────┘ │-BIW*[] │ └──────────────┘ 45 | ▲ │-FLEX Words[88]│ 46 | │ └───────────────┘ 47 | │ ▲ 48 | │ │ 49 | │ │ 50 | ┌───┴────┐ ┌───┴────┐ 51 | │FLEX FIW│ │FLEX BIW│ 52 | └────────┘ └────────┘ 53 | */ 54 | 55 | struct flexstatusstruct { 56 | uint8_t frame; 57 | uint8_t cycle; 58 | uint8_t state; 59 | uint8_t rxcollapse = 0; 60 | uint8_t collapse = 4; // 0 = all bits masked; 61 | uint8_t curblock; 62 | uint8_t curbit; 63 | uint32_t flexfrequency = 0; 64 | uint8_t curmessageno = 0; 65 | bool sendingflex = false; 66 | }; 67 | 68 | extern struct flexstatusstruct flexstatus; 69 | 70 | enum flexsynctype { SYNC_NOSYNC, 71 | SYNC_FLEX_1600, 72 | SYNC_FLEX_3200_2, 73 | SYNC_FLEX_3200_4, 74 | SYNC_FLEX_6400, 75 | SYNC_FLEX_RESYNC }; 76 | 77 | class flexsync { 78 | public: 79 | uint32_t FIWword; 80 | flexsynctype type; 81 | volatile bool isSynced; 82 | void IRAM_ATTR bitInterrupt(bool bit); 83 | void clearSync(); 84 | TaskHandle_t flexRXTaskId; 85 | 86 | protected: 87 | uint16_t bs1; 88 | uint32_t Aword; 89 | uint16_t Bword; 90 | uint32_t notAword; 91 | bool IRAM_ATTR validate(); 92 | }; 93 | 94 | class flexfiw { 95 | public: 96 | uint8_t priority = 0; 97 | uint8_t cycle = 0; 98 | uint8_t frame = 0; 99 | uint8_t repeat = 0; 100 | uint8_t traffic[4]; 101 | bool isValid = false; 102 | uint32_t output(); 103 | void input(uint32_t word); 104 | flexfiw(); 105 | flexfiw(uint32_t word); 106 | }; 107 | 108 | class flexbiw { 109 | public: 110 | uint8_t priority = 0; // 0 to 15 111 | uint8_t blocksize = 0; // 0 to 3 (but really, 1 to 4) 112 | uint8_t vectorstart = 0; // 0 to 63 113 | uint8_t carryon = 0; // 0 to 3 114 | uint8_t collapse = 0; // 0 to 7 (every 2^collapse frames); 115 | bool isValid = false; 116 | uint16_t localid = 0; 117 | uint8_t coveragezone = 0; 118 | struct time { 119 | uint8_t hours; 120 | uint8_t secs; 121 | uint8_t mins; 122 | uint8_t month; 123 | uint8_t day; 124 | uint16_t year; 125 | } time; 126 | enum biwtype_e { 127 | BIW, 128 | LOCAL_ID, 129 | DATE, 130 | TIME, 131 | COUNTRY, 132 | SYSTEM_INFO 133 | } biwtype; 134 | 135 | enum sysinfotype_e { 136 | MSG_ALL, 137 | MSG_HOME, 138 | MSG_ROAMING, 139 | MSG_SSID, 140 | MSG_TIME, 141 | SYS_TIME, 142 | CHANNEL_SETUP 143 | } sysinfo; 144 | flexbiw(); 145 | flexbiw(uint32_t word); 146 | flexbiw(enum biwtype_e type); 147 | uint32_t output(); 148 | void input(uint32_t in, uint8_t pos); 149 | }; 150 | 151 | class flexaddress { 152 | public: 153 | uint8_t frame = 0; 154 | uint8_t phase = 0; 155 | uint32_t ric = 0; 156 | uint8_t addresswords = 0; 157 | 158 | enum shortaddrtype { 159 | SHORT_INVALID, 160 | SHORT_IDLE1, 161 | SHORT_LONG_1, 162 | SHORT, 163 | SHORT_LONG_3, 164 | SHORT_LONG_4, 165 | SHORT_RESERVED1, 166 | SHORT_INFO_SERVICE, 167 | SHORT_NETWORK, 168 | SHORT_TEMP, 169 | SHORT_OPERATOR, 170 | SHORT_RESERVED2, 171 | SHORT_LONG_2, 172 | SHORT_IDLE2 173 | }; 174 | 175 | struct addrword { 176 | enum shortaddrtype type; 177 | uint32_t value; 178 | }; 179 | 180 | struct addrword word[2]; 181 | 182 | enum addrtype { ADDRT_INVALID, 183 | ADDRT_SHORT, 184 | ADDRT_1_2, 185 | ADDRT_1_3, 186 | ADDRT_1_4, 187 | ADDRT_2_3 } addrtype; 188 | 189 | enum addruse { ADDRUSE_SHORT, 190 | ADDRUSE_ILLEGAL, 191 | ADDRUSE_RESERVED1, 192 | ADDRUSE_INFORMATION_SVC_ADDR, 193 | ADDRUSE_NETWORK_ADDR, 194 | ADDRUSE_TEMPORARY, 195 | ADDRUSE_OPERATOR_MSG, 196 | ADDRUSE_RESERVED2, 197 | ADDRUSE_UNUSED, 198 | ADDRUSE_UNCOORDINATED, 199 | ADDRUSE_BY_COUNTRY1, 200 | ADDRUSE_GLOBAL1, 201 | ADDRUSE_GLOBAL2, 202 | ADDRUSE_GLOBAL3, 203 | ADDRUSE_BY_COUNTRY2, 204 | ADDRUSE_RESERVED3, 205 | ADDRUSE_INFO_SVC_GLOBAL, 206 | ADDRUSE_INFO_SVC_COUNTRY1, 207 | ADDRUSE_INFO_SVC_COUNTRY2, 208 | ADDRUSE_INFO_SVC_RESERVED } addruse; 209 | 210 | void decodeRIC(uint32_t address); 211 | void decodeRIC(uint32_t address, uint8_t offset); 212 | void decodeWord(uint32_t* wordp); 213 | void RICtoWord(uint32_t* wordp); 214 | 215 | static uint32_t temp(uint8_t tempaddr); 216 | static uint8_t getPhase(uint32_t address, char prefix); 217 | static uint8_t getFrame(uint32_t address, char prefix); 218 | static uint8_t getSubstract(char prefix); 219 | static uint8_t getNextFrame(uint32_t address, char prefix, uint8_t start, bool rxcollapse); 220 | flexaddress(); 221 | flexaddress(uint32_t* wordp); 222 | 223 | protected: 224 | enum shortaddrtype getShortType(uint32_t address); 225 | }; 226 | 227 | class flextempaddrmapping { 228 | public: 229 | std::vector addresslist; 230 | 231 | flextempaddrmapping(flexaddress addr_c, uint8_t frame_c, uint8_t tempaddr_c, uint8_t timeout_c); 232 | flextempaddrmapping(); 233 | ~flextempaddrmapping(); 234 | 235 | static flextempaddrmapping* getTempMapping(uint8_t frame, uint8_t tempaddr, bool allMappings); 236 | static void addTempMapping(uint8_t frame, uint8_t tempaddr, flexaddress* addr, uint8_t curframe); 237 | static void deleteTempMapping(flextempaddrmapping* mapping); 238 | static void removeStaleMappings(); 239 | 240 | protected: 241 | uint8_t tempaddr; 242 | uint8_t frame; 243 | uint8_t timeout; 244 | }; 245 | 246 | class flexvector { 247 | public: 248 | enum vectortype { 249 | VECTOR_INVALID, 250 | VECTOR_SECURE, 251 | VECTOR_SHORT_INSTRUCTION, 252 | VECTOR_SHORT_MESSAGE, 253 | VECTOR_NUMERIC, 254 | VECTOR_SPECIAL, 255 | VECTOR_ALPHA, 256 | VECTOR_HEX, 257 | VECTOR_NUMBEREDNUMERIC 258 | } type; 259 | 260 | // SHORT INSTRUCTION 261 | uint8_t tempAssignedFrame = 0; 262 | uint8_t tempAddress = 0; 263 | uint16_t systemEvent = 0; 264 | enum shorttype { 265 | VECTOR_SHORT_TEMP_ADDRESS, 266 | VECTOR_SYSTEM_EVENT 267 | } shortInstructionType; 268 | 269 | // DATA VECTOR 270 | uint8_t blockstart; 271 | uint8_t blocklength; 272 | uint8_t numchecksum; 273 | 274 | flexvector(uint32_t* wordp); 275 | static enum vectortype getVectorType(uint32_t* wordp); 276 | static enum vectortype getVectorType(uint32_t temp); 277 | static uint32_t getVector(uint8_t start, uint8_t blocks); 278 | static uint32_t getAlphaVector(uint8_t start, uint8_t blocks); 279 | static uint32_t getNumericVector(uint8_t start, uint8_t words, uint8_t checksum); 280 | static uint32_t getShortInstruction(uint8_t address, uint8_t frame); 281 | static uint32_t getShortInstruction(uint16_t event); 282 | 283 | //RX 284 | void decodeNumeric(uint32_t* wordp); 285 | void decodeShortInstruction(uint32_t* wordp); 286 | void decodeAlpha(uint32_t* wordp); 287 | void decodeShortMessage(uint32_t* wordp); 288 | }; 289 | 290 | class flexphase { 291 | public: 292 | enum idlestep { 293 | IDLE_1600, 294 | IDLE_3200_2, 295 | IDLE_3200_4_PHASE1, 296 | IDLE_3200_4_PHASE2, 297 | IDLE_6400_4_PHASE1, 298 | IDLE_6400_4_PHASE2, 299 | IDLE_6400_4_PHASE3, 300 | IDLE_6400_4_PHASE4 301 | }; 302 | struct flexblock { 303 | uint32_t word[8]; 304 | } block[11]; 305 | bool lowTraffic = false; 306 | bool errorMarker[88]; 307 | flexbiw biwone; 308 | uint8_t frame = 0; 309 | 310 | flexphase(void); 311 | flexphase(idlestep step); 312 | ~flexphase(void); 313 | 314 | flexdatablock* addBlock(flexdatablock* blockp); 315 | uint32_t getWord(uint8_t index); 316 | uint32_t* getWordP(uint8_t index); 317 | void prepPhaseForTX(void); 318 | void decode(std::vector* messages); 319 | flexbiw* addBIW(flexbiw::biwtype_e type); 320 | uint8_t usedWords(void); 321 | uint8_t freeWords(void); 322 | 323 | protected: 324 | std::vector biwarr; 325 | std::vector blockarr; 326 | }; 327 | 328 | struct flexdatablock { 329 | public: 330 | uint8_t wordcount; 331 | uint32_t* block; 332 | std::vector addrarr; 333 | uint32_t vectorwordtwo; 334 | uint8_t numchecksum; 335 | flexvector::vectortype type; 336 | flexdatablock(uint8_t words); 337 | ~flexdatablock(); 338 | static flexmsg* decode(flexphase* phase, uint8_t addrword, uint8_t curframe); 339 | uint8_t blockSize(); 340 | uint8_t addrBlockSize(); 341 | 342 | protected: 343 | static flexmsg* decodeAlpha(flexphase* phase, uint8_t addrword, uint8_t curframe, flexvector* vect); 344 | static flexmsg* decodeNumeric(flexphase* phase, uint8_t addrword, uint8_t curframe, flexvector* vect); 345 | }; 346 | 347 | class flexmsg { 348 | public: 349 | flexvector::vectortype type; 350 | uint8_t signature = 0; 351 | bool maildrop = false; 352 | bool retrieval = false; 353 | uint8_t messageno = 0; // doubles as temp address ID for short instruction vectors 354 | uint8_t fragmentid = 0x03; 355 | uint16_t wordcount = 0; 356 | 357 | // specifically RX 358 | uint32_t primaryAddress = 0; 359 | 360 | std::vector addrarr; 361 | 362 | void encode(flexvector::vectortype msgtype, char* datap); 363 | void encode(flexvector::vectortype msgtype, uint8_t tempaddress, uint8_t frame); 364 | flexdatablock* getBlock(uint8_t words); 365 | flexdatablock* getBlock(); 366 | 367 | void addAddress(uint32_t ric); 368 | uint8_t addrBlockSize(); 369 | 370 | ~flexmsg(); 371 | 372 | //RX functions 373 | void timeoutTrigger(); 374 | static void removeStaleMessages(); 375 | static void addIncompleteMessage(flexmsg* temp); 376 | static flexmsg* getIncomplete(uint32_t ric, uint8_t messageno); 377 | void addAlphaWords(flexphase* phase, uint8_t fragmentno, uint8_t blockstart, uint8_t blocksize); 378 | void addNumericData(uint8_t* databuffer, uint8_t characters); 379 | void addAddress(uint8_t curframe, flexaddress* addr); 380 | char* getAlphaData(); 381 | char* getNumericData(); 382 | 383 | protected: 384 | char* alphadata = NULL; 385 | char* numericdata = NULL; 386 | uint8_t vectorsize; // doubles as frame address for short instruction vectors 387 | uint16_t bytes = 0; 388 | uint16_t bytesleft = 0; 389 | 390 | char* readp = NULL; 391 | 392 | uint8_t timeout = 240; 393 | 394 | void encodeAlpha(char* datap); 395 | void encodeNumeric(char* datap); 396 | flexdatablock* getAlphaBlock(uint8_t words); 397 | flexdatablock* getNumericBlock(); 398 | flexdatablock* getShortInstructionBlock(); 399 | uint16_t countWords(char* start); 400 | void addTempMappings(uint8_t curframe, flexaddress* addr); // RX 401 | uint8_t encodeChar(char c); 402 | char decodeChar(uint8_t); 403 | }; 404 | 405 | class flexframe { 406 | public: 407 | RF4463* rf4463; 408 | uint32_t frequency; 409 | flexsynctype type; 410 | uint8_t multiplex; 411 | std::vector phase; 412 | flexfiw fiww; 413 | bool shortPreamble; 414 | void addByte(uint8_t byte); 415 | std::vector* decode(void); 416 | void startTX(void); 417 | static uint8_t getMultiplex(flexsynctype ftype); 418 | 419 | flexframe(flexsynctype ftype, uint32_t fiwc); 420 | flexframe(flexsynctype ftype); 421 | flexframe(); 422 | ~flexframe(); 423 | 424 | protected: 425 | uint16_t bitcount = 0; 426 | uint32_t fiwword; 427 | uint32_t getWord(); 428 | void queueTXBytes(const uint8_t* buffer, uint8_t len, flexsynctype txtype); 429 | void queueTXBytes(uint8_t* buffer, uint8_t len, flexsynctype txtype); 430 | void queueWord(uint32_t word, flexsynctype type); 431 | bool txBytesWait(uint8_t bytes); 432 | bool txTask(void); 433 | static void txTaskStart(void* _this); 434 | }; 435 | 436 | #endif -------------------------------------------------------------------------------- /POCSAG-Modem Eagle Files/eagle.epf: -------------------------------------------------------------------------------- 1 | [Eagle] 2 | Version="09 06 02" 3 | Platform="Windows" 4 | Globals="Globals" 5 | Desktop="Desktop" 6 | 7 | [Globals] 8 | AutoSaveProject=1 9 | UsedLibraryUrn="urn:adsk.eagle:library:79" 10 | UsedLibraryUrn="urn:adsk.eagle:library:88" 11 | UsedLibraryUrn="urn:adsk.eagle:library:201" 12 | UsedLibraryUrn="urn:adsk.eagle:library:202" 13 | UsedLibraryUrn="urn:adsk.eagle:library:203" 14 | UsedLibraryUrn="urn:adsk.eagle:library:204" 15 | UsedLibraryUrn="urn:adsk.eagle:library:206" 16 | UsedLibraryUrn="urn:adsk.eagle:library:208" 17 | UsedLibraryUrn="urn:adsk.eagle:library:210" 18 | UsedLibraryUrn="urn:adsk.eagle:library:211" 19 | UsedLibraryUrn="urn:adsk.eagle:library:212" 20 | UsedLibraryUrn="urn:adsk.eagle:library:213" 21 | UsedLibraryUrn="urn:adsk.eagle:library:89" 22 | UsedLibraryUrn="urn:adsk.eagle:library:214" 23 | UsedLibraryUrn="urn:adsk.eagle:library:215" 24 | UsedLibraryUrn="urn:adsk.eagle:library:218" 25 | UsedLibraryUrn="urn:adsk.eagle:library:220" 26 | UsedLibraryUrn="urn:adsk.eagle:library:221" 27 | UsedLibraryUrn="urn:adsk.eagle:library:222" 28 | UsedLibraryUrn="urn:adsk.eagle:library:224" 29 | UsedLibraryUrn="urn:adsk.eagle:library:226" 30 | UsedLibraryUrn="urn:adsk.eagle:library:229" 31 | UsedLibraryUrn="urn:adsk.eagle:library:230" 32 | UsedLibraryUrn="urn:adsk.eagle:library:90" 33 | UsedLibraryUrn="urn:adsk.eagle:library:231" 34 | UsedLibraryUrn="urn:adsk.eagle:library:233" 35 | UsedLibraryUrn="urn:adsk.eagle:library:234" 36 | UsedLibraryUrn="urn:adsk.eagle:library:235" 37 | UsedLibraryUrn="urn:adsk.eagle:library:236" 38 | UsedLibraryUrn="urn:adsk.eagle:library:237" 39 | UsedLibraryUrn="urn:adsk.eagle:library:238" 40 | UsedLibraryUrn="urn:adsk.eagle:library:239" 41 | UsedLibraryUrn="urn:adsk.eagle:library:240" 42 | UsedLibraryUrn="urn:adsk.eagle:library:241" 43 | UsedLibraryUrn="urn:adsk.eagle:library:93" 44 | UsedLibraryUrn="urn:adsk.eagle:library:242" 45 | UsedLibraryUrn="urn:adsk.eagle:library:243" 46 | UsedLibraryUrn="urn:adsk.eagle:library:244" 47 | UsedLibraryUrn="urn:adsk.eagle:library:245" 48 | UsedLibraryUrn="urn:adsk.eagle:library:248" 49 | UsedLibraryUrn="urn:adsk.eagle:library:250" 50 | UsedLibraryUrn="urn:adsk.eagle:library:251" 51 | UsedLibraryUrn="urn:adsk.eagle:library:252" 52 | UsedLibraryUrn="urn:adsk.eagle:library:255" 53 | UsedLibraryUrn="urn:adsk.eagle:library:256" 54 | UsedLibraryUrn="urn:adsk.eagle:library:94" 55 | UsedLibraryUrn="urn:adsk.eagle:library:258" 56 | UsedLibraryUrn="urn:adsk.eagle:library:259" 57 | UsedLibraryUrn="urn:adsk.eagle:library:260" 58 | UsedLibraryUrn="urn:adsk.eagle:library:261" 59 | UsedLibraryUrn="urn:adsk.eagle:library:262" 60 | UsedLibraryUrn="urn:adsk.eagle:library:263" 61 | UsedLibraryUrn="urn:adsk.eagle:library:264" 62 | UsedLibraryUrn="urn:adsk.eagle:library:265" 63 | UsedLibraryUrn="urn:adsk.eagle:library:266" 64 | UsedLibraryUrn="urn:adsk.eagle:library:267" 65 | UsedLibraryUrn="urn:adsk.eagle:library:96" 66 | UsedLibraryUrn="urn:adsk.eagle:library:269" 67 | UsedLibraryUrn="urn:adsk.eagle:library:270" 68 | UsedLibraryUrn="urn:adsk.eagle:library:272" 69 | UsedLibraryUrn="urn:adsk.eagle:library:273" 70 | UsedLibraryUrn="urn:adsk.eagle:library:274" 71 | UsedLibraryUrn="urn:adsk.eagle:library:275" 72 | UsedLibraryUrn="urn:adsk.eagle:library:276" 73 | UsedLibraryUrn="urn:adsk.eagle:library:278" 74 | UsedLibraryUrn="urn:adsk.eagle:library:279" 75 | UsedLibraryUrn="urn:adsk.eagle:library:281" 76 | UsedLibraryUrn="urn:adsk.eagle:library:97" 77 | UsedLibraryUrn="urn:adsk.eagle:library:283" 78 | UsedLibraryUrn="urn:adsk.eagle:library:284" 79 | UsedLibraryUrn="urn:adsk.eagle:library:285" 80 | UsedLibraryUrn="urn:adsk.eagle:library:287" 81 | UsedLibraryUrn="urn:adsk.eagle:library:288" 82 | UsedLibraryUrn="urn:adsk.eagle:library:289" 83 | UsedLibraryUrn="urn:adsk.eagle:library:290" 84 | UsedLibraryUrn="urn:adsk.eagle:library:291" 85 | UsedLibraryUrn="urn:adsk.eagle:library:293" 86 | UsedLibraryUrn="urn:adsk.eagle:library:294" 87 | UsedLibraryUrn="urn:adsk.eagle:library:98" 88 | UsedLibraryUrn="urn:adsk.eagle:library:296" 89 | UsedLibraryUrn="urn:adsk.eagle:library:297" 90 | UsedLibraryUrn="urn:adsk.eagle:library:298" 91 | UsedLibraryUrn="urn:adsk.eagle:library:300" 92 | UsedLibraryUrn="urn:adsk.eagle:library:302" 93 | UsedLibraryUrn="urn:adsk.eagle:library:303" 94 | UsedLibraryUrn="urn:adsk.eagle:library:304" 95 | UsedLibraryUrn="urn:adsk.eagle:library:307" 96 | UsedLibraryUrn="urn:adsk.eagle:library:312" 97 | UsedLibraryUrn="urn:adsk.eagle:library:313" 98 | UsedLibraryUrn="urn:adsk.eagle:library:99" 99 | UsedLibraryUrn="urn:adsk.eagle:library:314" 100 | UsedLibraryUrn="urn:adsk.eagle:library:315" 101 | UsedLibraryUrn="urn:adsk.eagle:library:317" 102 | UsedLibraryUrn="urn:adsk.eagle:library:318" 103 | UsedLibraryUrn="urn:adsk.eagle:library:319" 104 | UsedLibraryUrn="urn:adsk.eagle:library:320" 105 | UsedLibraryUrn="urn:adsk.eagle:library:322" 106 | UsedLibraryUrn="urn:adsk.eagle:library:323" 107 | UsedLibraryUrn="urn:adsk.eagle:library:324" 108 | UsedLibraryUrn="urn:adsk.eagle:library:325" 109 | UsedLibraryUrn="urn:adsk.eagle:library:102" 110 | UsedLibraryUrn="urn:adsk.eagle:library:327" 111 | UsedLibraryUrn="urn:adsk.eagle:library:328" 112 | UsedLibraryUrn="urn:adsk.eagle:library:331" 113 | UsedLibraryUrn="urn:adsk.eagle:library:332" 114 | UsedLibraryUrn="urn:adsk.eagle:library:334" 115 | UsedLibraryUrn="urn:adsk.eagle:library:335" 116 | UsedLibraryUrn="urn:adsk.eagle:library:336" 117 | UsedLibraryUrn="urn:adsk.eagle:library:337" 118 | UsedLibraryUrn="urn:adsk.eagle:library:338" 119 | UsedLibraryUrn="urn:adsk.eagle:library:339" 120 | UsedLibraryUrn="urn:adsk.eagle:library:80" 121 | UsedLibraryUrn="urn:adsk.eagle:library:105" 122 | UsedLibraryUrn="urn:adsk.eagle:library:341" 123 | UsedLibraryUrn="urn:adsk.eagle:library:342" 124 | UsedLibraryUrn="urn:adsk.eagle:library:343" 125 | UsedLibraryUrn="urn:adsk.eagle:library:344" 126 | UsedLibraryUrn="urn:adsk.eagle:library:346" 127 | UsedLibraryUrn="urn:adsk.eagle:library:347" 128 | UsedLibraryUrn="urn:adsk.eagle:library:350" 129 | UsedLibraryUrn="urn:adsk.eagle:library:351" 130 | UsedLibraryUrn="urn:adsk.eagle:library:353" 131 | UsedLibraryUrn="urn:adsk.eagle:library:358" 132 | UsedLibraryUrn="urn:adsk.eagle:library:107" 133 | UsedLibraryUrn="urn:adsk.eagle:library:360" 134 | UsedLibraryUrn="urn:adsk.eagle:library:361" 135 | UsedLibraryUrn="urn:adsk.eagle:library:362" 136 | UsedLibraryUrn="urn:adsk.eagle:library:364" 137 | UsedLibraryUrn="urn:adsk.eagle:library:366" 138 | UsedLibraryUrn="urn:adsk.eagle:library:367" 139 | UsedLibraryUrn="urn:adsk.eagle:library:368" 140 | UsedLibraryUrn="urn:adsk.eagle:library:369" 141 | UsedLibraryUrn="urn:adsk.eagle:library:371" 142 | UsedLibraryUrn="urn:adsk.eagle:library:372" 143 | UsedLibraryUrn="urn:adsk.eagle:library:109" 144 | UsedLibraryUrn="urn:adsk.eagle:library:373" 145 | UsedLibraryUrn="urn:adsk.eagle:library:374" 146 | UsedLibraryUrn="urn:adsk.eagle:library:375" 147 | UsedLibraryUrn="urn:adsk.eagle:library:376" 148 | UsedLibraryUrn="urn:adsk.eagle:library:377" 149 | UsedLibraryUrn="urn:adsk.eagle:library:378" 150 | UsedLibraryUrn="urn:adsk.eagle:library:379" 151 | UsedLibraryUrn="urn:adsk.eagle:library:380" 152 | UsedLibraryUrn="urn:adsk.eagle:library:382" 153 | UsedLibraryUrn="urn:adsk.eagle:library:383" 154 | UsedLibraryUrn="urn:adsk.eagle:library:111" 155 | UsedLibraryUrn="urn:adsk.eagle:library:385" 156 | UsedLibraryUrn="urn:adsk.eagle:library:386" 157 | UsedLibraryUrn="urn:adsk.eagle:library:387" 158 | UsedLibraryUrn="urn:adsk.eagle:library:388" 159 | UsedLibraryUrn="urn:adsk.eagle:library:389" 160 | UsedLibraryUrn="urn:adsk.eagle:library:391" 161 | UsedLibraryUrn="urn:adsk.eagle:library:392" 162 | UsedLibraryUrn="urn:adsk.eagle:library:393" 163 | UsedLibraryUrn="urn:adsk.eagle:library:394" 164 | UsedLibraryUrn="urn:adsk.eagle:library:396" 165 | UsedLibraryUrn="urn:adsk.eagle:library:112" 166 | UsedLibraryUrn="urn:adsk.eagle:library:397" 167 | UsedLibraryUrn="urn:adsk.eagle:library:398" 168 | UsedLibraryUrn="urn:adsk.eagle:library:399" 169 | UsedLibraryUrn="urn:adsk.eagle:library:400" 170 | UsedLibraryUrn="urn:adsk.eagle:library:401" 171 | UsedLibraryUrn="urn:adsk.eagle:library:402" 172 | UsedLibraryUrn="urn:adsk.eagle:library:403" 173 | UsedLibraryUrn="urn:adsk.eagle:library:404" 174 | UsedLibraryUrn="urn:adsk.eagle:library:407" 175 | UsedLibraryUrn="urn:adsk.eagle:library:408" 176 | UsedLibraryUrn="urn:adsk.eagle:library:113" 177 | UsedLibraryUrn="urn:adsk.eagle:library:409" 178 | UsedLibraryUrn="urn:adsk.eagle:library:410" 179 | UsedLibraryUrn="urn:adsk.eagle:library:411" 180 | UsedLibraryUrn="urn:adsk.eagle:library:412" 181 | UsedLibraryUrn="urn:adsk.eagle:library:417" 182 | UsedLibraryUrn="urn:adsk.eagle:library:418" 183 | UsedLibraryUrn="urn:adsk.eagle:library:419" 184 | UsedLibraryUrn="urn:adsk.eagle:library:527439" 185 | UsedLibraryUrn="urn:adsk.eagle:library:5610454" 186 | UsedLibraryUrn="urn:adsk.eagle:library:10015156" 187 | UsedLibraryUrn="urn:adsk.eagle:library:116" 188 | UsedLibraryUrn="urn:adsk.eagle:library:117" 189 | UsedLibraryUrn="urn:adsk.eagle:library:118" 190 | UsedLibraryUrn="urn:adsk.eagle:library:119" 191 | UsedLibraryUrn="urn:adsk.eagle:library:81" 192 | UsedLibraryUrn="urn:adsk.eagle:library:121" 193 | UsedLibraryUrn="urn:adsk.eagle:library:122" 194 | UsedLibraryUrn="urn:adsk.eagle:library:123" 195 | UsedLibraryUrn="urn:adsk.eagle:library:124" 196 | UsedLibraryUrn="urn:adsk.eagle:library:125" 197 | UsedLibraryUrn="urn:adsk.eagle:library:126" 198 | UsedLibraryUrn="urn:adsk.eagle:library:127" 199 | UsedLibraryUrn="urn:adsk.eagle:library:128" 200 | UsedLibraryUrn="urn:adsk.eagle:library:129" 201 | UsedLibraryUrn="urn:adsk.eagle:library:130" 202 | UsedLibraryUrn="urn:adsk.eagle:library:82" 203 | UsedLibraryUrn="urn:adsk.eagle:library:132" 204 | UsedLibraryUrn="urn:adsk.eagle:library:133" 205 | UsedLibraryUrn="urn:adsk.eagle:library:134" 206 | UsedLibraryUrn="urn:adsk.eagle:library:135" 207 | UsedLibraryUrn="urn:adsk.eagle:library:136" 208 | UsedLibraryUrn="urn:adsk.eagle:library:138" 209 | UsedLibraryUrn="urn:adsk.eagle:library:139" 210 | UsedLibraryUrn="urn:adsk.eagle:library:140" 211 | UsedLibraryUrn="urn:adsk.eagle:library:141" 212 | UsedLibraryUrn="urn:adsk.eagle:library:143" 213 | UsedLibraryUrn="urn:adsk.eagle:library:83" 214 | UsedLibraryUrn="urn:adsk.eagle:library:144" 215 | UsedLibraryUrn="urn:adsk.eagle:library:145" 216 | UsedLibraryUrn="urn:adsk.eagle:library:147" 217 | UsedLibraryUrn="urn:adsk.eagle:library:148" 218 | UsedLibraryUrn="urn:adsk.eagle:library:149" 219 | UsedLibraryUrn="urn:adsk.eagle:library:150" 220 | UsedLibraryUrn="urn:adsk.eagle:library:151" 221 | UsedLibraryUrn="urn:adsk.eagle:library:152" 222 | UsedLibraryUrn="urn:adsk.eagle:library:153" 223 | UsedLibraryUrn="urn:adsk.eagle:library:154" 224 | UsedLibraryUrn="urn:adsk.eagle:library:84" 225 | UsedLibraryUrn="urn:adsk.eagle:library:155" 226 | UsedLibraryUrn="urn:adsk.eagle:library:156" 227 | UsedLibraryUrn="urn:adsk.eagle:library:157" 228 | UsedLibraryUrn="urn:adsk.eagle:library:158" 229 | UsedLibraryUrn="urn:adsk.eagle:library:159" 230 | UsedLibraryUrn="urn:adsk.eagle:library:160" 231 | UsedLibraryUrn="urn:adsk.eagle:library:161" 232 | UsedLibraryUrn="urn:adsk.eagle:library:162" 233 | UsedLibraryUrn="urn:adsk.eagle:library:163" 234 | UsedLibraryUrn="urn:adsk.eagle:library:164" 235 | UsedLibraryUrn="urn:adsk.eagle:library:85" 236 | UsedLibraryUrn="urn:adsk.eagle:library:165" 237 | UsedLibraryUrn="urn:adsk.eagle:library:166" 238 | UsedLibraryUrn="urn:adsk.eagle:library:167" 239 | UsedLibraryUrn="urn:adsk.eagle:library:169" 240 | UsedLibraryUrn="urn:adsk.eagle:library:170" 241 | UsedLibraryUrn="urn:adsk.eagle:library:171" 242 | UsedLibraryUrn="urn:adsk.eagle:library:172" 243 | UsedLibraryUrn="urn:adsk.eagle:library:173" 244 | UsedLibraryUrn="urn:adsk.eagle:library:174" 245 | UsedLibraryUrn="urn:adsk.eagle:library:175" 246 | UsedLibraryUrn="urn:adsk.eagle:library:86" 247 | UsedLibraryUrn="urn:adsk.eagle:library:176" 248 | UsedLibraryUrn="urn:adsk.eagle:library:177" 249 | UsedLibraryUrn="urn:adsk.eagle:library:179" 250 | UsedLibraryUrn="urn:adsk.eagle:library:180" 251 | UsedLibraryUrn="urn:adsk.eagle:library:181" 252 | UsedLibraryUrn="urn:adsk.eagle:library:182" 253 | UsedLibraryUrn="urn:adsk.eagle:library:183" 254 | UsedLibraryUrn="urn:adsk.eagle:library:184" 255 | UsedLibraryUrn="urn:adsk.eagle:library:188" 256 | UsedLibraryUrn="urn:adsk.eagle:library:189" 257 | UsedLibraryUrn="urn:adsk.eagle:library:87" 258 | UsedLibraryUrn="urn:adsk.eagle:library:190" 259 | UsedLibraryUrn="urn:adsk.eagle:library:191" 260 | UsedLibraryUrn="urn:adsk.eagle:library:192" 261 | UsedLibraryUrn="urn:adsk.eagle:library:193" 262 | UsedLibraryUrn="urn:adsk.eagle:library:194" 263 | UsedLibraryUrn="urn:adsk.eagle:library:195" 264 | UsedLibraryUrn="urn:adsk.eagle:library:196" 265 | UsedLibraryUrn="urn:adsk.eagle:library:197" 266 | UsedLibraryUrn="urn:adsk.eagle:library:198" 267 | UsedLibraryUrn="urn:adsk.eagle:library:199" 268 | UsedLibrary="C:/Users/Jelmer Bruijn/Documents/EAGLE/libraries/Nomeo-Transceivers.lbr" 269 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/_hhn_con_pinhead_box_1mm27.lbr" 270 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Transistor.lbr" 271 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Switch.lbr" 272 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-sensor.lbr" 273 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Resistor.lbr" 274 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Relay.lbr" 275 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-ic.lbr" 276 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Inductor.lbr" 277 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Moudle.lbr" 278 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-fuse.lbr" 279 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Display.lbr" 280 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Diode.lbr" 281 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Crystal-Oscillator.lbr" 282 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Connector.lbr" 283 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Capacitor.lbr" 284 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Button.lbr" 285 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/seeed/Seeed-OPL-Antenna.lbr" 286 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/CONNECTER.lbr" 287 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/Eagle-JeeLabs.lbr" 288 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/eagle-ltspice.lbr" 289 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/MadLabs-DigitalIC.lbr" 290 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/L9110.lbr" 291 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/IV-11.lbr" 292 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/jelmer.lbr" 293 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-Displays.lbr" 294 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-Sensors.lbr" 295 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-RF.lbr" 296 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-Resistors.lbr" 297 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-DigitalIC.lbr" 298 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-Connectors.lbr" 299 | UsedLibrary="Z:/Documents/Documenten Jelmer/projects/eagle/lbr/SparkFun-Aesthetics.lbr" 300 | UsedLibrary="C:/Users/Jelmer Bruijn/Documents/EAGLE/libraries/esp32.lbr" 301 | 302 | [Win_1] 303 | Type="Control Panel" 304 | Number=0 305 | 306 | [Desktop] 307 | Screen="3440 1440" 308 | Window="Win_1" 309 | -------------------------------------------------------------------------------- /ESP32-firmware/src/httpd.cpp: -------------------------------------------------------------------------------- 1 | #include "httpd.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | //#include 8 | #include 9 | #include 10 | 11 | #include "ESPAsyncWebServer.h" 12 | #include "RF4463.h" 13 | #include "decoder.h" 14 | #include "encoder.h" 15 | #include "jwifi.h" 16 | #include "pagerkeepalive.h" 17 | #include "settings.h" 18 | #include "statustracker.h" 19 | 20 | AsyncWebServer server(80); 21 | AsyncWebSocket ws("/ws"); 22 | bool HTTPrestartNeeded = false; 23 | 24 | void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { 25 | sendStatus(STATUS_WIFI_ACTIVITY); 26 | switch (type) { 27 | case WS_EVT_CONNECT: 28 | //client connected 29 | ets_printf("ws[%s][%u] connect\n", server->url(), client->id()); 30 | //client->ping(); 31 | sendStatus(STATUS_NEW_WS_CONNECTION); 32 | break; 33 | case WS_EVT_DISCONNECT: 34 | //client disconnected 35 | ets_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id()); 36 | break; 37 | case WS_EVT_ERROR: 38 | //error was received from the other end 39 | ets_printf("WS Error received :(\n\n"); 40 | //ets_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data); 41 | break; 42 | case WS_EVT_PONG: 43 | //pong message was received (in response to a ping request maybe) 44 | ets_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : ""); 45 | break; 46 | case WS_EVT_DATA: 47 | //data packet 48 | AwsFrameInfo *info = (AwsFrameInfo *)arg; 49 | if (info->final && info->index == 0 && info->len == len) { 50 | //the whole message is in a single frame and we got all of it's data 51 | ets_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); 52 | if (info->opcode == WS_TEXT) { 53 | data[len] = 0; 54 | ets_printf("%s\n", (char *)data); 55 | } else { 56 | for (size_t i = 0; i < info->len; i++) { 57 | ets_printf("%02x ", data[i]); 58 | } 59 | ets_printf("\n"); 60 | } 61 | if (info->opcode == WS_TEXT) 62 | client->text("{\"status\":\"received\"}"); 63 | else 64 | client->binary("{\"status\":\"received\"}"); 65 | } else { 66 | //message is comprised of multiple frames or the frame is split into multiple packets 67 | if (info->index == 0) { 68 | if (info->num == 0) 69 | ets_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); 70 | ets_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); 71 | } 72 | 73 | ets_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); 74 | if (info->message_opcode == WS_TEXT) { 75 | data[len] = 0; 76 | ets_printf("%s\n", (char *)data); 77 | } else { 78 | for (size_t i = 0; i < len; i++) { 79 | ets_printf("%02x ", data[i]); 80 | } 81 | ets_printf("\n"); 82 | } 83 | 84 | if ((info->index + len) == info->len) { 85 | ets_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); 86 | if (info->final) { 87 | ets_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); 88 | if (info->message_opcode == WS_TEXT) 89 | client->text("{\"status\":\"received\"}"); 90 | else 91 | client->binary("{\"status\":\"received\"}"); 92 | } 93 | } 94 | } 95 | break; 96 | } 97 | } 98 | 99 | void webSocketSendProcess(void *parameter) { 100 | uint32_t ulNotificationValue; 101 | status.websocketUpdater = xTaskGetCurrentTaskHandle(); 102 | while (true) { 103 | ulNotificationValue = ulTaskNotifyTake(pdTRUE, 1000 / portTICK_RATE_MS); 104 | if (ulNotificationValue == 0) { // timeout, so every 1s 105 | ws.cleanupClients(); 106 | } else { 107 | if (ws.count()) 108 | sendStatus(STATUS_WIFI_ACTIVITY); 109 | DynamicJsonDocument doc(1500); 110 | if (ulNotificationValue & WS_SEND_MODE_STATUS) { 111 | doc["rxActive"] = status.rxActive; 112 | doc["txActive"] = status.txActive; 113 | doc["freq"] = status.freq; 114 | doc["txMode"] = status.currentmode; 115 | } 116 | if (ulNotificationValue & WS_SEND_SIGNAL) { 117 | doc["rxSignal"] = status.rxSignal; 118 | } 119 | if (ulNotificationValue & WS_SEND_MODMODE) { 120 | switch (settings.current.mode) { 121 | case 0: 122 | doc["mode"] = "MODE_FLEX"; 123 | break; 124 | case 1: 125 | doc["mode"] = "MODE_POCSAG"; 126 | break; 127 | case 2: 128 | doc["mode"] = "MODE_TX_ONLY"; 129 | break; 130 | case 3: 131 | doc["mode"] = "MODE_FLEX_SYNCED"; 132 | break; 133 | } 134 | doc["baud"] = settings.current.baud; 135 | } 136 | if (ulNotificationValue & WS_SEND_FRAME_STATUS) { // flex frame counter update 137 | JsonArray statusframes = doc.createNestedArray("frames"); 138 | for (uint8_t c = 0; c < STATUSFRAMELISTSIZE; c++) { 139 | if (statusframearr[c]) { 140 | JsonObject statusframe = statusframes.createNestedObject(); 141 | statusframe["frame"] = statusframearr[c]->frameno; 142 | statusframe["isTX"] = statusframearr[c]->isTX; 143 | statusframe["freq"] = statusframearr[c]->freq; 144 | statusframe["txSkipped"] = statusframearr[c]->txCancelled; 145 | switch (statusframearr[c]->rxtype) { 146 | case flexsynctype::SYNC_FLEX_1600: 147 | statusframe["rxType"] = "FLEX_1600"; 148 | break; 149 | case flexsynctype::SYNC_FLEX_3200_2: 150 | statusframe["rxType"] = "FLEX_3200_2"; 151 | break; 152 | case flexsynctype::SYNC_FLEX_3200_4: 153 | statusframe["rxType"] = "FLEX_3200_4"; 154 | break; 155 | case flexsynctype::SYNC_FLEX_6400: 156 | statusframe["rxType"] = "FLEX_3200_4"; 157 | break; 158 | default: 159 | break; 160 | } 161 | switch (statusframearr[c]->txformat) { 162 | case txframe::FORMAT_FLEX: 163 | statusframe["txType"] = "FLEX"; 164 | break; 165 | case txframe::FORMAT_POCSAG: 166 | statusframe["txType"] = "POCSAG"; 167 | break; 168 | case txframe::FORMAT_IDLE: 169 | statusframe["txType"] = "IDLE"; 170 | break; 171 | case txframe::FORMAT_BLOCKED: 172 | statusframe["txType"] = "BLOCKED"; 173 | break; 174 | default: 175 | break; 176 | } 177 | } 178 | } 179 | } 180 | size_t len = measureJson(doc); 181 | xSemaphoreTake(wsMutex, portMAX_DELAY); 182 | auto buffer = std::make_shared>(len); 183 | serializeJson(doc, buffer->data(), len); 184 | ws.textAll(buffer); 185 | xSemaphoreGive(wsMutex); 186 | } 187 | } 188 | } 189 | 190 | void httpdProcess(void *parameter) { 191 | xTaskCreate(webSocketSendProcess, "webSocketSendProcess", 10000, NULL, 5, NULL); 192 | wsMutex = xSemaphoreCreateMutex(); 193 | bool daemonRunning = false; 194 | vTaskDelay(5000 / portTICK_PERIOD_MS); 195 | while (true) { 196 | if (HTTPrestartNeeded) { 197 | if (daemonRunning) { 198 | server.end(); 199 | daemonRunning = false; 200 | } 201 | initHTTPD(); 202 | daemonRunning = true; 203 | HTTPrestartNeeded = false; 204 | } 205 | if (wifiConnected == true && !daemonRunning) { 206 | initHTTPD(); 207 | daemonRunning = true; 208 | } else if (!wifiConnected && daemonRunning) { 209 | server.end(); 210 | } 211 | // 212 | vTaskDelay(20000 / portTICK_PERIOD_MS); 213 | } 214 | } 215 | 216 | void notFound(AsyncWebServerRequest *request) { 217 | sendStatus(STATUS_WIFI_ACTIVITY); 218 | //List all parameters 219 | int params = request->params(); 220 | for (int i = 0; i < params; i++) { 221 | AsyncWebParameter *p = request->getParam(i); 222 | if (p->isFile()) { //p->isPost() is also true 223 | Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); 224 | } else if (p->isPost()) { 225 | Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); 226 | } else { 227 | Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); 228 | } 229 | } 230 | 231 | AsyncResponseStream *response = request->beginResponseStream("text/html"); 232 | response->print("

      Send Message

      "); 233 | response->print("Frequency:
      "); 234 | response->print("Speed:
      "); 235 | response->print("RIC: Function:
      "); 237 | response->print("Type:
      "); 238 | response->print("
      "); 239 | 240 | response->print("

      "); 241 | response->print("Frequency:
      "); 242 | response->print("RIC: Numeric
      "); 243 | response->print("
      "); 244 | 245 | //response->print("Captive Portal"); 246 | //response->print("

      This is out captive portal front page.

      "); 247 | //response->printf("

      You were trying to reach: http://%s%s

      ", request->host().c_str(), request->url().c_str()); 248 | //response->printf("

      Try opening this link instead

      ", WiFi.softAPIP().toString().c_str()); 249 | response->print(""); 250 | request->send(response); 251 | } 252 | 253 | void sendPOCSAG(AsyncWebServerRequest *request) { 254 | String message; 255 | StaticJsonDocument<100> data; 256 | message = request->getParam("text", true)->value(); 257 | request->getParam("text", true)->value(); 258 | uint32_t ric = request->getParam("ric", true)->value().toInt(); 259 | uint16_t baud = request->getParam("speed", true)->value().toInt(); 260 | uint8_t func = request->getParam("func", true)->value().toInt(); 261 | uint32_t freq = request->getParam("freq", true)->value().toInt(); 262 | uint8_t type = request->getParam("type", true)->value().toInt(); 263 | enum pocsagaddr::type sendtype = pocsagaddr::TONE; 264 | enum pocsagaddr::func sendfunc = pocsagaddr::A; 265 | switch (type) { 266 | case 0: 267 | sendtype = pocsagaddr::NUMERIC; 268 | break; 269 | case 1: 270 | sendtype = pocsagaddr::TONE; 271 | break; 272 | case 3: 273 | sendtype = pocsagaddr::ALPHA; 274 | break; 275 | } 276 | switch (func) { 277 | case 0: 278 | sendfunc = pocsagaddr::A; 279 | break; 280 | case 1: 281 | sendfunc = pocsagaddr::B; 282 | break; 283 | case 2: 284 | sendfunc = pocsagaddr::C; 285 | break; 286 | case 3: 287 | sendfunc = pocsagaddr::D; 288 | break; 289 | } 290 | 291 | if (RF4463::checkAvail(freq, RF4463::rftype::POCSAG_2400_TX)) { 292 | pocsagaddr adr(ric, sendtype, sendfunc); 293 | int frame = addPOCSAG(&adr, freq, baud, (char *)message.c_str()); 294 | if (frame == -1) { 295 | data["error"] = "NO_SLOTS_AVAILABLE"; 296 | } else { 297 | data["status"] = "QUEUED"; 298 | data["transmissionBeginFrame"] = frame; 299 | } 300 | } else { 301 | data["error"] = "FREQUENCY_NOT_AVAIL"; 302 | } 303 | String response; 304 | serializeJson(data, response); 305 | request->send(200, "application/json", response); 306 | } 307 | 308 | void sendFLEX(AsyncWebServerRequest *request) { 309 | String message; 310 | StaticJsonDocument<100> data; 311 | message = request->getParam("text", true)->value(); 312 | request->getParam("text", true)->value(); 313 | uint32_t ric = request->getParam("ric", true)->value().toInt(); 314 | uint32_t freq = request->getParam("freq", true)->value().toInt(); 315 | bool isNumeric = false; 316 | if(request->hasParam("isnumeric",true)){ 317 | isNumeric = true; 318 | } 319 | if (RF4463::checkAvail(freq, RF4463::rftype::FLEX_6400_TX)) { 320 | int frame = addFLEX('E', ric, freq, (char *)message.c_str(), isNumeric); 321 | if (frame == -1) { 322 | data["error"] = "NO_SLOTS_AVAILABLE"; 323 | } else { 324 | data["status"] = "QUEUED"; 325 | data["transmissionBeginFrame"] = frame; 326 | } 327 | } else { 328 | data["error"] = "FREQUENCY_NOT_AVAIL"; 329 | } 330 | String response; 331 | serializeJson(data, response); 332 | request->send(200, "application/json", response); 333 | } 334 | 335 | void resetwifi(AsyncWebServerRequest *request) { 336 | wifiResetConfig(); 337 | } 338 | 339 | void initHTTPD() { 340 | Serial.print("Starting HTTPD... "); 341 | //WiFi.softAP("esp-captive"); 342 | //dnsServer.start(53, "*", WiFi.softAPIP()); 343 | //server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP 344 | //more handlers... 345 | server.onNotFound(notFound); 346 | ws.onEvent(onEvent); 347 | server.addHandler(&ws); 348 | server.on("/sendpocsag", HTTP_POST, sendPOCSAG); 349 | server.on("/sendflex", HTTP_POST, sendFLEX); 350 | server.on("/settings", HTTP_GET, getSettings); 351 | server.on("/forgetwifi", HTTP_GET, resetwifi); 352 | server.on("/settings", HTTP_POST, saveSettingsComplete, NULL, saveSettings); 353 | server.on("/rxmode", HTTP_GET, getMode); 354 | server.on("/rxmode", HTTP_POST, getMode, NULL, setMode); 355 | server.on("/idlelist", HTTP_GET, getIdleList); 356 | server.on("/idlelist", HTTP_POST, getIdleList, NULL, delFromIdleList); 357 | server.serveStatic("/", LittleFS, "/www/"); 358 | //server.onRequestBody(saveSettings); 359 | server.begin(); 360 | Serial.print("Running on "); 361 | Serial.print(WiFi.localIP()); 362 | Serial.println(""); 363 | } 364 | -------------------------------------------------------------------------------- /ESP32-firmware/src/pocsagutils.cpp: -------------------------------------------------------------------------------- 1 | #include "pocsagutils.h" 2 | 3 | #include 4 | 5 | #include "bitutils.h" 6 | 7 | #define DEBUG 8 | 9 | extern QueueHandle_t pocsagQueue; 10 | // pocsag addr 11 | uint32_t pocsagaddr::getWord() { 12 | uint32_t ret = 0; 13 | switch (func) { 14 | case A: 15 | ret |= addValue(19, 2, 0); 16 | break; 17 | case B: 18 | ret |= addValue(19, 2, 2); 19 | break; 20 | case C: 21 | ret |= addValue(19, 2, 1); 22 | break; 23 | case D: 24 | ret |= addValue(19, 2, 3); 25 | break; 26 | }; 27 | uint32_t tric = ric; 28 | tric >>= 3; 29 | tric <<= 13; 30 | ret |= tric; 31 | ret = createcrc(ret); 32 | return ret; 33 | } 34 | pocsagaddr::pocsagaddr(uint32_t addrword, uint8_t word) { 35 | } 36 | uint32_t pocsagaddr::decodeAddress(uint32_t word, uint8_t wordcount) { 37 | word >>= 11; 38 | switch (word & 0x03) { 39 | case 0x00: 40 | func = pocsagaddr::A; 41 | break; 42 | case 0x01: 43 | func = pocsagaddr::B; 44 | break; 45 | case 0x02: 46 | func = pocsagaddr::C; 47 | break; 48 | case 0x03: 49 | func = pocsagaddr::D; 50 | break; 51 | } 52 | word >>= 2; 53 | word <<= 3; 54 | word |= (wordcount / 2) & 0x03; 55 | ric = word; 56 | return word; 57 | } 58 | pocsagaddr::pocsagaddr(uint32_t ric_c, enum type type_c, enum func function_c) { 59 | ric = ric_c; 60 | frameno = ric & 0x07; 61 | type = type_c; 62 | func = function_c; 63 | } 64 | pocsagaddr::pocsagaddr() { 65 | } 66 | // pocsag data 67 | pocsagdata::pocsagdata() { 68 | } 69 | pocsagdata::pocsagdata(pocsagaddr ric, char* data) { 70 | addMsg(ric, data); 71 | } 72 | pocsagdata::~pocsagdata() { 73 | if (batch) free(batch); 74 | if (errorflags) free(errorflags); 75 | } 76 | uint16_t pocsagdata::requiredSize(pocsagaddr ric, char* data) { 77 | uint16_t wordsneeded = 1; // we always need 1 address word; 78 | uint32_t len = strlen(data); 79 | uint16_t framesneeded = 0; 80 | 81 | switch (ric.type) { 82 | case pocsagaddr::ALPHA: 83 | wordsneeded += (len * 7) / 20; 84 | if ((len * 7) % 20) { 85 | wordsneeded++; 86 | } 87 | break; 88 | case pocsagaddr::NUMERIC: 89 | wordsneeded += (len * 4) / 20; 90 | if ((len * 4) % 20) { 91 | wordsneeded++; 92 | } 93 | break; 94 | case pocsagaddr::TONE: 95 | break; 96 | } 97 | if (batchcount == 0) { 98 | framesneeded++; 99 | if (((8 - ric.frameno) * 2) < wordsneeded) { 100 | wordsneeded -= ((8 - ric.frameno) * 2); 101 | } else { 102 | wordsneeded = 0; 103 | } 104 | } else { 105 | if (batch[batchcount - 1].frame[ric.frameno].word[0] != IDLEWORD) { 106 | if (batch[batchcount - 1].frame[ric.frameno].word[1] != IDLEWORD) { 107 | // new frame 108 | framesneeded++; 109 | if (((8 - ric.frameno) * 2) < wordsneeded) { 110 | wordsneeded -= ((8 - ric.frameno) * 2); 111 | } else { 112 | wordsneeded = 0; 113 | } 114 | } else { 115 | // second free 116 | if (((8 - ric.frameno) * 2) - 1 < wordsneeded) { 117 | wordsneeded -= ((8 - ric.frameno) * 2) - 1; 118 | } else { 119 | wordsneeded = 0; 120 | } 121 | } 122 | } else { 123 | if (((8 - ric.frameno) * 2) < wordsneeded) { 124 | wordsneeded -= ((8 - ric.frameno) * 2); 125 | } else { 126 | wordsneeded = 0; 127 | } 128 | // first word free 129 | } 130 | } 131 | //wordsneeded++; 132 | framesneeded += wordsneeded / 16; 133 | if (wordsneeded % 16) { 134 | framesneeded++; 135 | } else if (((wordsneeded % 16) == 0) && (wordsneeded > 0)) { 136 | framesneeded++; // hmmm. 137 | } 138 | //ets_printf("Length: %u Frames needed: %u - words needed %u\n", len, framesneeded, wordsneeded); 139 | return framesneeded; 140 | } 141 | void pocsagdata::addMsg(pocsagaddr ric, char* data) { 142 | uint16_t curbatch = batchcount - 1; 143 | uint16_t curword = 0; 144 | if (batchcount == 0) curbatch = 0; 145 | growBatches(requiredSize(ric, data)); 146 | if (batch[batchcount - 1].frame[ric.frameno].word[0] != IDLEWORD) { 147 | if (batch[batchcount - 1].frame[ric.frameno].word[1] != IDLEWORD) { 148 | // new frame 149 | curbatch++; 150 | curword = ric.frameno * 2; 151 | } else { 152 | // second free 153 | curword = (ric.frameno * 2) + 1; 154 | } 155 | } else { 156 | // first word free 157 | curword = ric.frameno * 2; 158 | } 159 | ((uint32_t*)&(batch[curbatch]))[curword] = ric.getWord(); 160 | curword++; 161 | uint32_t tempword = 0; 162 | switch (ric.type) { 163 | case pocsagaddr::ALPHA: 164 | uint8_t bitsleft, bitalign; 165 | bitalign = 0; 166 | while (*data != 0x00) { 167 | tempword = 0; 168 | bitsleft = 20; 169 | if (bitalign) { // if bitalign is not 0, there were bits for a previous character left. Shift them in! 170 | tempword |= bitswitch(((*data) & 0x7F)); 171 | bitsleft -= bitalign; 172 | bitalign = 0; 173 | if (*data != 0) { 174 | data++; 175 | } 176 | } 177 | // Now lets see how many full characters we can fit in there (usually 1 or 2) 178 | while ((*data != 0) && (bitsleft >= 7)) { 179 | tempword <<= 7; 180 | tempword |= bitswitch(((*data) & 0x7F)); 181 | data++; 182 | bitsleft -= 7; 183 | } 184 | while ((*data == 0) && (bitsleft >= 7)) { 185 | tempword <<= 7; 186 | tempword |= bitswitch(((0x00) & 0x7F)); 187 | //data++; //hmmm 188 | bitsleft -= 7; 189 | } 190 | // Now 'OR' the first bits of the next character onto the frame, and set how many bits we've fit 191 | // in there in bitalign 192 | tempword = (tempword << bitsleft); 193 | bitalign = 7 - bitsleft; 194 | if (*data != 0) { 195 | tempword |= (bitswitch(((*data) & 0x7F)) >> bitalign); 196 | } else { 197 | tempword |= (bitswitch(((0x00) & 0x7F)) >> bitalign); 198 | } 199 | tempword = (tempword << 11); 200 | tempword |= 0x80000000; 201 | tempword = createcrc(tempword); 202 | //ets_printf("Storing %u into %u , %u \n", tempword, curbatch, curword); 203 | ((uint32_t*)&(batch[curbatch]))[curword] = tempword; 204 | curword++; 205 | } 206 | break; 207 | case pocsagaddr::TONE: 208 | break; 209 | case pocsagaddr::NUMERIC: 210 | uint8_t charcount; 211 | while ((*data != 0)) { // loop for as long as messagep isn't 0 212 | charcount = 0; 213 | tempword = 0; 214 | while ((*data != 0) && (charcount < 5)) { // check if the pointer doesnt point to the 0 character, and load characters as long as it doesnt. 215 | tempword <<= 4; 216 | tempword |= encodeNumber(*data); 217 | data++; 218 | charcount++; 219 | } 220 | while (charcount < 5) { // if we enter this block, we ran out of characters before the frame was full. Fill it up with 0-pointers 221 | tempword <<= 4; 222 | tempword |= 0x03; 223 | charcount++; 224 | } 225 | // calculate CRC for the frame 226 | tempword = (tempword << 11); 227 | tempword |= 0x80000000; 228 | tempword = createcrc(tempword); 229 | ((uint32_t*)&(batch[curbatch]))[curword] = tempword; 230 | curword++; 231 | } 232 | break; 233 | } 234 | } 235 | void pocsagdata::growBatches(uint16_t growby) { 236 | //ets_printf("GrowBatches called with %u \n", growby); 237 | if (batch != nullptr) { 238 | batch = (struct batchs*)realloc(batch, (batchcount + growby) * sizeof(struct batchs)); // Common realloc mistake: 'batch' nulled but not freed upon failure 239 | errorflags = (uint16_t*)realloc(errorflags, (batchcount + growby) * sizeof(uint16_t)); // this one too. I kinda assume the realloc never fails. If it does, the shit hits the fan 240 | } else { 241 | batch = (struct batchs*)calloc(growby, sizeof(struct batchs)); 242 | errorflags = (uint16_t*)calloc(growby, sizeof(uint16_t)); 243 | } 244 | for (uint8_t c = 0; c < growby; c++) { 245 | for (uint8_t d = 0; d < 8; d++) { 246 | batch[(batchcount + c)].frame[d].word[0] = IDLEWORD; 247 | batch[(batchcount + c)].frame[d].word[1] = IDLEWORD; 248 | } 249 | } 250 | batchcount += growby; 251 | //ets_printf("Batchcount is now %u \n", batchcount); 252 | } 253 | void pocsagdata::addWord(uint32_t word, uint8_t wordcount, bool hasErrors) { 254 | bool doDecode = false; 255 | batchs* batchcopyp; 256 | uint8_t batchcountcopy; 257 | uint16_t msgwordcountcopy; 258 | uint16_t* errorflagspcopy; 259 | switch (getType(word)) { 260 | case pocsagdata::wordtype::TYPE_IDLE: 261 | if (msgStarted) { 262 | batchcopyp = batch; 263 | batchcountcopy = batchcount; 264 | msgwordcountcopy = msgwordcount; 265 | errorflagspcopy = errorflags; 266 | 267 | batch = nullptr; 268 | errorflags = nullptr; 269 | batchcount = 0; 270 | msgStarted = false; 271 | doDecode = true; 272 | } 273 | break; 274 | case pocsagdata::wordtype::TYPE_ADDR: 275 | if (msgStarted) { 276 | batchcopyp = batch; 277 | batchcountcopy = batchcount; 278 | msgwordcountcopy = msgwordcount; 279 | errorflagspcopy = errorflags; 280 | 281 | errorflags = nullptr; 282 | batch = nullptr; 283 | batchcount = 0; 284 | doDecode = true; 285 | } else { 286 | msgStarted = true; 287 | } 288 | growBatches(1); 289 | msgwordcount = 1; 290 | ((uint32_t*)&(batch[batchcount - 1]))[wordcount] = word; 291 | if (hasErrors) errorflags[batchcount - 1] |= (1 << wordcount); 292 | break; 293 | case pocsagdata::wordtype::TYPE_MSG: 294 | if (msgStarted) { 295 | msgwordcount++; 296 | if (wordcount == 0) { 297 | growBatches(1); 298 | } 299 | ((uint32_t*)&(batch[batchcount - 1]))[wordcount] = word; 300 | } 301 | break; 302 | } 303 | if (doDecode) decodeMessage(batchcopyp, batchcountcopy, msgwordcountcopy, errorflagspcopy); // send pointer to decodeMessage; the batch block is freed there. 304 | } 305 | uint8_t pocsagdata::getFrameSize() { 306 | uint8_t ret = 0; 307 | uint32_t totalbits = 1000 * ((17 * 32 * batchcount) + 576) / baudrate; 308 | ret = (totalbits / 1875); 309 | if (totalbits % 1875) ret++; 310 | return ret; 311 | } 312 | uint8_t pocsagdata::getFrameSize(uint16_t batchcount) { 313 | uint8_t ret = 0; 314 | uint32_t totalbits = 1000 * ((17 * 32 * batchcount) + 576) / baudrate; 315 | ret = (totalbits / 1875); 316 | if (totalbits % 1875) ret++; 317 | return ret; 318 | } 319 | uint8_t pocsagdata::encodeNumber(char c) { 320 | // This is a lookup for the relevant numbers for alpha pagers. 321 | switch (c) { 322 | case 0x30: 323 | return 0; 324 | case 0x31: 325 | return 0x08; 326 | case 0x32: 327 | return 0x04; 328 | case 0x33: 329 | return 0x0C; 330 | case 0x34: 331 | return 0x02; 332 | case 0x35: 333 | return 0x0A; 334 | case 0x36: 335 | return 0x06; 336 | case 0x37: 337 | return 0x0E; 338 | case 0x38: 339 | return 0x01; 340 | case 0x39: 341 | return 0x09; 342 | case 0x2F: //spare is slash 343 | case 0x41: // A 344 | return 0x05; 345 | case 0x55: // U is urgent? 346 | case 0x42: // B 347 | return 0x0D; 348 | case 0x20: // space 349 | case 0x43: // C 350 | return 0x03; 351 | case 0x2D: // -- 352 | case 0x44: // D 353 | return 0x0B; 354 | case 0x5D: // [ 355 | case 0x45: // E 356 | return 0x07; 357 | case 0x5B: // ] 358 | case 0x46: // F 359 | return 0x0F; 360 | default: 361 | return 0x05; 362 | break; 363 | } 364 | } 365 | char pocsagdata::decodeNumber(uint8_t c) { 366 | c &= 0x0F; 367 | switch (c) { 368 | case 8: 369 | return '1'; 370 | case 4: 371 | return '2'; 372 | case 12: 373 | return '3'; 374 | case 2: 375 | return '4'; 376 | case 10: 377 | return '5'; 378 | case 6: 379 | return '6'; 380 | case 14: 381 | return '7'; 382 | case 1: 383 | return '8'; 384 | case 9: 385 | return '9'; 386 | case 0: 387 | return '0'; 388 | case 5: 389 | return '/'; 390 | case 13: 391 | return 'U'; 392 | case 3: 393 | return ' '; 394 | case 11: 395 | return '-'; 396 | case 7: 397 | return ']'; 398 | case 15: 399 | return '['; 400 | } 401 | return ' '; 402 | } 403 | pocsagdata::wordtype pocsagdata::getType(uint32_t word) { 404 | if (isIdle(word)) return pocsagdata::wordtype::TYPE_IDLE; 405 | if (isAddr(word)) return pocsagdata::wordtype::TYPE_ADDR; 406 | return pocsagdata::wordtype::TYPE_MSG; 407 | } 408 | bool pocsagdata::isMsg(uint32_t word) { 409 | if (!isIdle(word)) { 410 | return ((word & 0x80000000)); 411 | } else { 412 | return false; 413 | } 414 | } 415 | bool pocsagdata::isAddr(uint32_t word) { 416 | if (!isIdle(word)) { 417 | return (!(word & 0x80000000)); 418 | } 419 | return false; 420 | } 421 | bool pocsagdata::isIdle(uint32_t word) { 422 | return (word == 2055848343); 423 | } 424 | void pocsagdata::decodeMessage(batchs* batchp, uint8_t batchcount, uint16_t wordcount, uint16_t* errorflagsp) { 425 | uint32_t temp; 426 | pocsagaddr addr; 427 | 428 | uint16_t alphacount = 2 + (((wordcount - 1) * 20) / 7); 429 | uint16_t numcount = 2 + (wordcount * 5); 430 | uint8_t errorMarker = 0; 431 | char* alphabuffer = (char*)calloc(1, alphacount); 432 | char* numbuffer = (char*)calloc(1, numcount); 433 | char* alphabufferp = alphabuffer; 434 | char* numbufferp = numbuffer; 435 | 436 | uint32_t tempw = 0; 437 | char tempchar; 438 | char unaligned = 0; 439 | uint8_t bitsleft; 440 | uint8_t rxbitalign = 0; 441 | uint8_t unalignederror = 0; 442 | 443 | for (uint8_t c = 0; c < batchcount; c++) { 444 | for (uint8_t d = 0; d < 16; d++) { 445 | temp = ((uint32_t*)&(batchp[c]))[d]; 446 | if (isAddr(temp)) { 447 | addr.decodeAddress(temp, d); 448 | } else if (isMsg(temp)) { // Decode msg word 449 | temp &= 0x7FFFFFFF; 450 | tempw = temp; 451 | if (errorflagsp[c] & (1 << d)) { 452 | errorMarker = 0x80; 453 | } else { 454 | errorMarker = 0x00; 455 | } 456 | // decode as numeric 457 | *numbufferp = errorMarker | decodeNumber((temp >> 27)); 458 | numbufferp++; 459 | *numbufferp = errorMarker | decodeNumber((temp >> 23)); 460 | numbufferp++; 461 | *numbufferp = errorMarker | decodeNumber((temp >> 19)); 462 | numbufferp++; 463 | *numbufferp = errorMarker | decodeNumber((temp >> 15)); 464 | numbufferp++; 465 | *numbufferp = errorMarker | decodeNumber((temp >> 11)); 466 | numbufferp++; 467 | // decode as alpha 468 | bitsleft = 20; 469 | while (bitsleft) { 470 | if (rxbitalign != 0) { 471 | tempchar = (char)(tempw >> (bitsleft + 4 + (7 - rxbitalign))) & 0x7F; 472 | tempchar |= unaligned; 473 | bitsleft -= rxbitalign; 474 | rxbitalign = 0; 475 | tempchar = bitswitch(tempchar); 476 | *alphabufferp = (tempchar | errorMarker) | unalignederror; 477 | alphabufferp++; 478 | } else { 479 | if (bitsleft >= 7) { 480 | tempchar = (char)(tempw >> (bitsleft + 4)) & 0x7F; 481 | tempchar = bitswitch(tempchar); 482 | *alphabufferp = tempchar | errorMarker; 483 | alphabufferp++; 484 | bitsleft -= 7; 485 | } else { 486 | unaligned = (char)(tempw >> 11) & 0x7F; 487 | rxbitalign = 7 - bitsleft; 488 | unaligned <<= rxbitalign; 489 | unaligned &= 0x7F; 490 | bitsleft = 0; 491 | unalignederror = errorMarker; 492 | } 493 | } 494 | } // end while bitsleft loop 495 | } // end if msg 496 | } // end word-loop 497 | } // end batch-loop 498 | *alphabufferp = 0x00; 499 | *numbufferp = 0x00; 500 | pocsagmsg* msg = new pocsagmsg(addr, alphabuffer, numbuffer); 501 | queueMessage(msg); 502 | if (batchp) free(batchp); 503 | if (errorflagsp) free(errorflagsp); 504 | } 505 | int pocsagdata::getRating(char* alpha, char* numeric, pocsagaddr addr) { 506 | int rating = 0; 507 | if (addr.func == pocsagaddr::A) { 508 | rating -= 250; 509 | } else if (addr.func == pocsagaddr::D) { 510 | rating += 250; 511 | } 512 | uint16_t alphalen = 0; 513 | uint16_t nonprint = 0; 514 | alphalen = strlen(alpha); 515 | for (uint16_t c = 0; c < alphalen; c++) { 516 | if ((alpha[c] & 0x7F) < 0x20) { 517 | nonprint++; 518 | } 519 | } 520 | if ((alpha[alphalen - 1] & 0x7F) == 0x00) { 521 | nonprint--; 522 | if ((alpha[alphalen - 2] & 0x7F) == 0x00) { 523 | nonprint--; 524 | } 525 | } 526 | 527 | if (nonprint == 0) { 528 | rating += 200; 529 | } else if (nonprint < 2) { 530 | rating += 100; 531 | } else if (nonprint >= 3) { 532 | rating -= 200; 533 | } else if (nonprint >= 2) { 534 | rating -= 100; 535 | } 536 | 537 | uint16_t numericlen = 0; 538 | uint16_t nonnumbers = 0; 539 | numericlen = strlen(numeric)|1; 540 | for (uint16_t c = 0; c < numericlen; c++) { 541 | if (((numeric[c] & 0x7F) >= 0x30) && ((numeric[c] & 0x7F) <= 0x39)) { 542 | } else { 543 | nonnumbers++; 544 | } 545 | } 546 | for (uint8_t c = 0; c < 4; c++) { 547 | if ((numeric[(numericlen - 1) - c] & 0x7F) == 0x20) { 548 | nonnumbers--; 549 | } else { 550 | break; 551 | } 552 | } 553 | if (nonnumbers > (numericlen / 4)) { 554 | rating += 100; 555 | } else { 556 | rating -= 100; 557 | } 558 | if (numericlen - nonnumbers == 10) { 559 | rating -= 100; 560 | } 561 | 562 | if (numericlen > 60) { 563 | rating += 400; 564 | } else if (numericlen > 40) { 565 | rating += 200; 566 | } else if (numericlen > 20) { 567 | rating += 100; 568 | } 569 | return rating; 570 | } 571 | void pocsagdata::queueMessage(pocsagmsg* msg) { 572 | if (msg) { 573 | BaseType_t queuestatus = xQueueSend(pocsagQueue, &msg, 0); 574 | if (queuestatus == pdFALSE) { 575 | delete msg; 576 | ets_printf("!!! Failed to queue message. Maybe the processing queue has stalled?\n"); 577 | } 578 | msg = nullptr; 579 | } 580 | } 581 | 582 | pocsagmsg::~pocsagmsg() { 583 | delete alphadata; 584 | delete numdata; 585 | } 586 | pocsagmsg::pocsagmsg(pocsagaddr ad, char* alpha, char* num) { 587 | addr = ad; 588 | alphadata = alpha; 589 | numdata = num; 590 | } --------------------------------------------------------------------------------