├── .gitignore ├── Makefile ├── OTAdual.h ├── README.md ├── esp8266-rs41.ino └── img ├── putty-serial-setup.png ├── putty-telnet.png ├── rs41-connector.png ├── rs41-wemos.png ├── rs41.png ├── wififing.png ├── wifilist.png └── wifimainpage.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | wifiinfo.h 2 | credentials.h 3 | build 4 | manifest.txt 5 | *~ 6 | *.old 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Arduino based scketches 2 | # 3 | # Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io 4 | # MIT License - see License.txt file 5 | # 6 | # This Makefile uses the arduino-cli, the Arduino command line interface 7 | # and has been designed and tested to run on Linux, not on Windows. 8 | # Probably it will run on a Mac, but it has not been tested. 9 | # 10 | # Please note that: 11 | # 12 | # 1. each sketch must reside in his own folder with this Makefile 13 | # 14 | # 2. the main make targets are: 15 | # - all compiles and upload 16 | # - compile compiles only 17 | # - upload upload via serial port, compile if the binary file is 18 | # not available 19 | # - ota upload Over The Air, automatically find the device 20 | # IP address using the IOT_NAME (device hostname) 21 | # - clean clean the build directory 22 | # - find find OTA updatable devices on the local subnet 23 | # - requirements it the file "requirements.txt" exists it will 24 | # install the libraries listed in this file 25 | # 26 | # default is "all" 27 | # 28 | # 3. it gets the name of the sketch using the wildcard make command; 29 | # the name is *.ino; this means that you must have ONLY a file 30 | # with .ino extension, otherwise this makefile will break. This 31 | # also means that you can use this Makefile, almost unmodified, 32 | # for any sketch as long as you keep a single .ino file in each 33 | # folder 34 | # 35 | # 4. you can split your project in multiple files, if you wish, 36 | # using a single .ino file and multiple .h files, that you can 37 | # include in the .ino file with an '#include "myfile.h"' 38 | # directive 39 | # 40 | # Optionally some environment variables can be set: 41 | # 42 | # FQBN Fully Qualified Board Name; if not set in the environment 43 | # it will be assigned a value in this makefile 44 | # 45 | # SERIAL_DEV Serial device to upload the sketch; if not set in the 46 | # environment it will be assigned: 47 | # /dev/ttyUSB0 if it exists, or 48 | # /dev/ttyACM0 if it exists, or 49 | # unknown 50 | # 51 | # IOT_NAME Name of the IOT device; if not set in the environment 52 | # it will be assigned a value in this makefile. This is 53 | # very useful for OTA update, the device will be searched 54 | # on the local subnet using this name 55 | # 56 | # OTA_PORT Port used by OTA update; if not set in the environment 57 | # it will be assigned the default value of 8266 in this 58 | # makefile 59 | # 60 | # OTA_PASS Password used for OTA update; if not set in the environment 61 | # it will be assigned the default value of an empty string 62 | # 63 | # V verbose flag; can be 0 (quiet) or 1 (verbose); if not set 64 | # in the environment it will be assigned a default value 65 | # in this makefile 66 | 67 | 68 | MAKE_DIR := $(PWD) 69 | # 70 | # ----- setup wor Wemos D1 mini ----- 71 | FQBN ?= esp8266:esp8266:d1_mini 72 | IOT_NAME ?= esp8266-rs41-fcf8b3 73 | OTA_PORT ?= 8266 74 | OTA_PASS ?= 75 | # ----- setup for Arduino Uno 76 | #FQBN ?= arduino:avr:uno 77 | # ----- --------------------- 78 | V ?= 0 79 | VFLAG = 80 | 81 | ifeq "$(V)" "1" 82 | VFLAG =-v 83 | endif 84 | 85 | ifndef SERIAL_DEV 86 | ifneq (,$(wildcard /dev/ttyUSB0)) 87 | SERIAL_DEV = /dev/ttyUSB0 88 | else ifneq (,$(wildcard /dev/ttyACM0)) 89 | SERIAL_DEV = /dev/ttyACM0 90 | else 91 | SERIAL_DEV = unknown 92 | endif 93 | endif 94 | 95 | BUILD_DIR := $(subst :,.,build/$(FQBN)) 96 | 97 | SRC := $(wildcard *.ino) 98 | HDRS := $(wildcard *.h) 99 | BIN := $(BUILD_DIR)/$(SRC).bin 100 | ELF := $(BUILD_DIR)/$(SRC).elf 101 | 102 | $(info FQBN is [${FQBN}]) 103 | $(info IOT_NAME is [${IOT_NAME}]) 104 | $(info OTA_PORT is [${OTA_PORT}]) 105 | $(info OTA_PASS is [${OTA_PASS}]) 106 | $(info V is [${V}]) 107 | $(info VFLAG is [${VFLAG}]) 108 | $(info MAKE_DIR is [${MAKE_DIR}]) 109 | $(info BUILD_DIR is [${BUILD_DIR}]) 110 | $(info SRC is [${SRC}]) 111 | $(info HDRS is [${HDRS}]) 112 | $(info BIN is [${BIN}]) 113 | $(info SERIAL_DEV is [${SERIAL_DEV}]) 114 | 115 | all: $(ELF) upload 116 | .PHONY: all 117 | 118 | compile: $(ELF) 119 | .PHONY: compile 120 | 121 | $(ELF): $(SRC) $(HDRS) 122 | arduino-cli compile -b $(FQBN) $(VFLAG) 123 | @if which arduino-manifest.pl; \ 124 | then echo "---> Generating manifest.txt"; \ 125 | arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt.new; \ 126 | if diff manifest.txt manifest.txt.new > /dev/null; \ 127 | then echo "---> manifest.txt is up to date (has not changed)"; \ 128 | rm -f manifest.txt.new; \ 129 | else mv -f manifest.txt.new manifest.txt; \ 130 | fi; \ 131 | else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \ 132 | echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \ 133 | fi 134 | 135 | upload: 136 | @if [ ! -c $(SERIAL_DEV) ] ; \ 137 | then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \ 138 | else echo "---> Uploading sketch\n"; \ 139 | arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \ 140 | fi 141 | 142 | ota: 143 | @PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \ 144 | PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \ 145 | IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \ 146 | BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \ 147 | echo "PLAT_PATH is [$$PLAT_PATH]" ; \ 148 | echo "PY_PATH: is [$$PY_PATH]" ; \ 149 | echo "IOT_IP: is [$$IOT_IP]" ; \ 150 | echo "BINFILE: is [$$BINFILE]" ; \ 151 | if [ "$$IOT_IP" = "" ] ; \ 152 | then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \ 153 | else echo "---> Uploading Over The Air"; \ 154 | $$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\ 155 | fi 156 | 157 | clean: 158 | @echo "---> Cleaning the build directory" 159 | rm -rf build 160 | 161 | find: 162 | avahi-browse _arduino._tcp --resolve --parsable --terminate 163 | 164 | requirements: 165 | @if [ -e requirements.txt ]; \ 166 | then while read -r i ; do echo ; \ 167 | echo "---> Installing " '"'$$i'"' ; \ 168 | arduino-cli lib install "$$i" ; \ 169 | done < requirements.txt ; \ 170 | else echo "---> MISSING requirements.txt file"; \ 171 | fi 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /OTAdual.h: -------------------------------------------------------------------------------- 1 | // This file works on ESP8266, not tested on ESP32 2 | // 3 | #ifdef ESP32 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | 13 | #include 14 | #include 15 | 16 | #if defined(ESP32_RTOS) && defined(ESP32) 17 | void taskOne( void * parameter ) 18 | { 19 | ArduinoOTA.handle(); 20 | delay(3500); 21 | } 22 | #endif 23 | 24 | // global variables and definitions ------------ 25 | #define WIFICLIENT 0 26 | #define WIFIAP 1 27 | int wifimode; // can be WIFICLIENT or WIFIAP 28 | String ssidList; // list to be included in the form to choose the available ssid 29 | const IPAddress apIP(192, 168, 1, 1); 30 | const char* apSSID = "esp8266_rs41_SETUP"; 31 | DNSServer dnsServer; 32 | // --------------------------------------------- 33 | 34 | IPAddress getAPip() { 35 | return(apIP); 36 | } 37 | 38 | int getWifiMode() { 39 | return(wifimode); 40 | } 41 | 42 | String getSsidList() { 43 | return(ssidList); 44 | } 45 | 46 | void scanNetworks(const char* current_ssid) { 47 | char s[30]; 48 | delay(100); 49 | int n = WiFi.scanNetworks(); 50 | delay(100); 51 | for (int i = 0; i < n; ++i) { 52 | ssidList += ""; 66 | Serial.println(WiFi.SSID(i)); 67 | } 68 | delay(100); 69 | } 70 | 71 | void setupOTA(const char* nameprefix, const char* ssid, const char* password) { 72 | const int maxlen = 40; 73 | char fullhostname[maxlen]; 74 | uint8_t mac[6]; 75 | WiFi.macAddress(mac); 76 | snprintf(fullhostname, maxlen, "%s-%02x%02x%02x", nameprefix, mac[3], mac[4], mac[5]); 77 | ArduinoOTA.setHostname(fullhostname); 78 | 79 | WiFi.mode(WIFI_STA); 80 | WiFi.begin(ssid, password); 81 | if (WiFi.waitForConnectResult() == WL_CONNECTED) { // successfully connected as client to an AP 82 | Serial.print("Wifi client connection successfull to ssid: ");Serial.println(ssid); 83 | wifimode = WIFICLIENT; 84 | scanNetworks(ssid); 85 | } else { // become an access point 86 | wifimode = WIFIAP; 87 | Serial.print("Wifi client connection failed to ssid: ");Serial.println(ssid); 88 | WiFi.disconnect(); 89 | scanNetworks(""); 90 | WiFi.mode(WIFI_AP); 91 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 92 | WiFi.softAP(apSSID); 93 | dnsServer.start(53, "*", apIP); 94 | } 95 | 96 | // Port defaults to 3232 97 | // ArduinoOTA.setPort(3232); 98 | 99 | // No authentication by default 100 | // ArduinoOTA.setPassword("admin"); 101 | 102 | // Password can be set with it's md5 value as well 103 | // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 104 | // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); 105 | 106 | ArduinoOTA.onStart([]() { 107 | String type; 108 | if (ArduinoOTA.getCommand() == U_FLASH) 109 | type = "sketch"; 110 | else // U_SPIFFS 111 | type = "filesystem"; 112 | 113 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 114 | Serial.println("Start updating " + type); 115 | }); 116 | ArduinoOTA.onEnd([]() { 117 | Serial.println("\nEnd"); 118 | }); 119 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { 120 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 121 | }); 122 | ArduinoOTA.onError([](ota_error_t error) { 123 | Serial.printf("Error[%u]: ", error); 124 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 125 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 126 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 127 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 128 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 129 | }); 130 | 131 | ArduinoOTA.begin(); 132 | 133 | Serial.println("OTA Initialized"); 134 | Serial.print("IP address: "); 135 | Serial.println(WiFi.localIP()); 136 | 137 | #if defined(ESP32_RTOS) && defined(ESP32) 138 | xTaskCreate( 139 | ota_handle, /* Task function. */ 140 | "OTA_HANDLE", /* String with name of task. */ 141 | 10000, /* Stack size in bytes. */ 142 | NULL, /* Parameter passed as input of the task */ 143 | 1, /* Priority of the task. */ 144 | NULL); /* Task handle. */ 145 | #endif 146 | } 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | If you are a 4 | [radiosonde hunter](https://www.wired.com/story/radiosonde-hunters/) 5 | and a ham radio operator and have found one, or more, 6 | [Vaisala RS-41](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41) 7 | [Radiosonde](https://en.wikipedia.org/wiki/Radiosonde) and want to 8 | re-use them with the original firmware, but operating on radio amateur 9 | frequencies, this is the project for you. 10 | 11 | This project use a 12 | [Wemos D1 Mini](https://www.wemos.cc/en/latest/d1/d1_mini.html), an 13 | arduino-like board that is based on the WiFi enabled chip ESP8266, to 14 | automatically setup an amateur radio frequency on the RS-41 at each 15 | boot. 16 | 17 | This board is small enough to fit inside the original polystyrene 18 | shell, with some minor shell modifications. 19 | 20 | This small board will be powered by the RS-41 and, soon after boot, will: 21 | 22 | - modify the RS-41 registers to set the selected frequency in the 70 23 | cm radio amateur band through the serial interface; 24 | - try to connect to the pre-selected WiFi access point, if this fail 25 | it will start in Access Point mode and will accept a client 26 | connection from a mobile phone or laptop; 27 | - start a web server to allow: 28 | - the selection of the RS-41 transmit frequency, related register 29 | values are calculated from frequency entered 30 | - the time needed to go in deep sleep mode to save energy 31 | - and the WiFi SSID and password to connect to, at the next reboot; 32 | - start also a telnet server that will act as a bridge between the 33 | telnet connection and the RS-41 serial interface; this will allow an 34 | easy modification of other RS-41 parameters like the callsign or the 35 | initial TX power. These parameters will be stored by the RS-41 and 36 | will survive reboots. 37 | 38 | # RS41 connector pinout 39 | 40 | ![RS-41 connector](./img/rs41.png) 41 | 42 | The RS-41 has a connector on the front, an unusual connector with a 2 43 | mm pin pitch instead of the usual 2.54 mm (0.1 inch), this means that 44 | it is quite difficult to attach the typical "Dupont" jumper wires, so 45 | common in the Arduino environemt, that have a 2.54 mm pitch. 46 | 47 | The pinout of this connector is: 48 | 49 | 1. **GND** - Ground 50 | 2. **UART RX** - Serial port RX, TTL levels (0V / 3.3V) 51 | 3. **UART TX** - Serial port TX, TTL levels (0V / 3.3V) 52 | 4. **PB1** - Unknown purpose 53 | 5. **Vcc bust** - About 3.75V when the Radiosonde is ON, 0V when it is OFF 54 | 6. **Vcc** - About 3.3V, goes down to about 3V when batteries are 55 | low. The value remains about the same when the Radiosonde is ON or 56 | is OFF; 57 | 7. **RESET** - Reset to the MCU, often used by the SWD interface 58 | probes, active low 59 | 8. **SWCLK** - Clock of the 60 | [SWD (Serial Wire Debug) interface](https://wiki.segger.com/SWD) of 61 | the 62 | [STM32F100C8](https://www.st.com/en/microcontrollers-microprocessors/stm32f100c8.html) 63 | micro-controller 64 | 9. **SWDIO** - Data Input/Output of the [SWD (Serial Wire Debug) interface](https://wiki.segger.com/SWD) 65 | 10. **GND** - Ground 66 | 67 | # The Serial Interface 68 | 69 | It is possible to connect the RS41 serial interface to a PC using a 70 | USB TTL Serial Adapter, 71 | [(something similar to this)](https://www.amazon.com/USB-Serial-Adapter-Converter-Communication/dp/B0766383FL); 72 | it is important to pay attention at the adapater voltage, it must be 73 | 3.3V. If you use a 5V adapter, probably you will destroy your RS41. 74 | 75 | You can use a terminal emulator software, like the excellent 76 | [PuTTY](https://www.putty.org/), with the following connection settings: 77 | 78 | * speed: 9600 baud 79 | * parity: none 80 | * number of bits: 8 81 | * number of stop bits: 1 82 | * hardware flow control: none 83 | 84 | The RS41 uses a single Carriage Return byte (hex 0D, decimal 13, char 85 | ^M) as end of line marker, instead of the more common 2 bytes sequence 86 | of Carriage Return and Line Feed; for this reason it is important to 87 | set accordingly the terminal emulator. In the case of PuTTY the 88 | settings are shown on the following screenshots: 89 | 90 | ![Putty serial setup](./img/putty-serial-setup.png) 91 | 92 | On Windows PC the serial line will be something like COM6, COM7 or 93 | similar name, you can look at the "Device Manager" to find the correct 94 | serial interface name. 95 | 96 | ## The Boot message 97 | 98 | With the serial interface connected to the PC and the terminal 99 | emulator software running, if we power-on the RS41 we get 100 | the following text on the terminal: 101 | 102 | ``` 103 | Vaisala RS41 Radiosonde SW V2.02.14 104 | Copyright (c) Vaisala Oyj 2016. All rights reserved. 105 | Serial number: NOCALL-0 106 | Transmitter frequency: 404.80 MHz 107 | Transmitter power: 0/7 108 | 109 | Enabled TX 110 | ``` 111 | 112 | ## The hidden service menu 113 | 114 | Few smart peoples (like Rolf DF9DQ and Mirko IZ4PNN) have found a way 115 | to access the RS41 configuration menu through the UART interface: 116 | after the above text has been printed, wait few seconds and then type 117 | the following string, that will not be echoed back: 118 | 119 | ``` 120 | Enter (press the "Enter" key) 121 | STwsv 122 | Enter (press the "Enter" key) 123 | ``` 124 | 125 | I have found that the above keys must be typed slowly, if you type too 126 | fast the service menu will not be shown. 127 | 128 | The following service menu will be printed on the terminal: 129 | 130 | ``` 131 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 132 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 133 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 134 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 135 | > 136 | ``` 137 | 138 | ## The most interesting service menu options 139 | 140 | From an amateur radio operator point of view, wishing to re-use the 141 | RS41 Radiosonde in the amateur radio bands the most interesting 142 | commands are those that allows to change the callsign (or *serial 143 | number*) and the frequency. 144 | 145 | ### Change the Radiosonde callsign 146 | 147 | Typing the "i" key to select the "**Ser(i)al no**" menu, it is 148 | possible to replace the serial number of the Radiosonde with our own 149 | ham radio callsign: 150 | 151 | ``` 152 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 153 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 154 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 155 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 156 | >i 157 | Serial number P2720454 >IU4OKB-6 158 | ``` 159 | 160 | This setting will survive reboots. But pay attention to not broadcast 161 | your callsign outside of the amateur radio bands! 162 | 163 | ### Change the Radiosonde transmit frequency 164 | 165 | Typing the "f" key to select the "**TX (f)requency**" service menu option 166 | it is possible to change the TX frequency, but only from 400 Mhz to 167 | 405.99 Mhz. To select a frequency inside one of the radio amateur 168 | bands there is a workaround using the TX registers (see below). 169 | 170 | ``` 171 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 172 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 173 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 174 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 175 | >f 176 | TX frequency 404.60 >404.80 177 | ``` 178 | 179 | This setting will survive reboots. 180 | 181 | ### Enable/Disable transmission 182 | 183 | Typing the "x" key to select the "**T(X) state**" it is possible to 184 | disable the radio transmission and to re-enable it. This can be useful 185 | during our testing of the Radiosonde interface, to prevent 186 | transmissions that could be picked-up by some nearby receiver and 187 | forwarded to sites like radiosondy.info. 188 | 189 | ``` 190 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 191 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 192 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 193 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 194 | >x 195 | TX disabled 196 | ``` 197 | 198 | This setting **will not** survive reboots. 199 | 200 | ### Select the transmission power 201 | 202 | Typing the "o" key to select the "**TX p(o)wer**" it is possible to 203 | select a value from 0 to 7 to select the transmission power. To be 204 | precise this is the transmission power when the Radiosonde is not in 205 | flight mode, when it will enter "flight mode", probably above 1000 m 206 | altitude, it will switch to maximum power. 207 | 208 | This value will go into one of the 209 | [Si4032](https://www.silabs.com/documents/public/data-sheets/Si4030-31-32.pdf) 210 | registers and, according to his databook, the transmission power is 211 | listed below, but please note that the actual power can be different 212 | because it depends also on the actual circuit and PCB layout: 213 | 214 | |Value|Powr dBm|Power mW| 215 | |:---:|-------:|-------:| 216 | |0 | +1 dBm| 1.3 mW| 217 | |1 | +2 dBm| 1.6 mW| 218 | |2 | +5 dBm| 3.2 mW| 219 | |3 | +8 dBm| 6.3 mW| 220 | |4 | +11 dBm| 12.6 mW| 221 | |5 | +14 dBm| 25.1 mW| 222 | |6 | +17 dBm| 50.1 mW| 223 | |7 | +20 dBm|100.0 mW| 224 | 225 | It can be useful to reduce the power to the minum level during our 226 | testing with the Radiosonde, to reduce the risk that the transmissions 227 | could be picked-up by some nearby receiver and forwarded to sites like 228 | radiosondy.info. 229 | 230 | ``` 231 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 232 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 233 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 234 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 235 | >o 236 | TX power (0-7) 3 >0 237 | ``` 238 | 239 | This setting will survive reboots. 240 | 241 | ### Select the frequency inside the 70 cm radio amateur band 242 | 243 | One interesting service menu is the "**(T)X registers**", typing the 244 | "t" key, it is possible to change the value of one of the 7F available 245 | registers. These are the 246 | [Si4032](https://www.silabs.com/documents/public/data-sheets/Si4030-31-32.pdf) 247 | registers, this means that changing these values it is possible to 248 | modify the beahviour of tha radio chip; of particular interest is 249 | changing the value of the registers 0x75, 0x76 and 0x77 that are 250 | responsible for the TX frequency. Modifying these registers is 251 | possible to select a frequency from 208 Mhz to 832 Mhz. This seems 252 | different from what is printed on the 253 | [Si4032 datasheet](https://www.silabs.com/documents/public/data-sheets/Si4030-31-32.pdf) 254 | (from 240 Mhz to 960 Mhz), but the reason is that the datasheet 255 | assumes a PCB with a 30 Mhz crystal, instead on the RS41 there is a 26 256 | Mhz crystal, for this reason all the frequencies listed on the 257 | datasheet must be "re-scaled" multiplying them by 26/30. This also 258 | means that it's not possible to use the 33 cm radio amateur band, but 259 | only the 70 cm band. 260 | 261 | How to calculate the value of these registers is explained in the 262 | datasheet and in an excel spreadsheet available on the Silicon Labs 263 | website. But in our case it is easier to use the 264 | [calculator](https://www.makemehack.com/2020/12/how-to-change-the-tx-frequency-of-the-vaisala-rs41-radiosonde.html) 265 | availble on my website. Anyway in this project the software running on 266 | the Wemos D1 mini does this calculation and there is non need to know 267 | the values of these registers. 268 | 269 | An example changing these registers to set 433.500 Mhz is the 270 | following (register 0x75=0x21, 0x76=0x02 and 0x77=0x67): 271 | 272 | ``` 273 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 274 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 275 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 276 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 277 | >t 278 | Register number (00-7F) >75 279 | Register value 16 >21 280 | OK 281 | 282 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 283 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 284 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 285 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 286 | >t 287 | Register number (00-7F) >76 288 | Register value B0 >02 289 | OK 290 | 291 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 292 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 293 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 294 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 295 | >t 296 | Register number (00-7F) >77 297 | Register value EC >67 298 | OK 299 | ``` 300 | 301 | These settings **will not** survive reboots, this is the reason why 302 | this project was born! 303 | 304 | # The software on this repository 305 | 306 | The software on this repository is developed with the Aduino 307 | toolchain, it is included in the *esp8266-rs41.ino* and in the include 308 | file *OTAdual.h*, these are the only two files needed to compile the 309 | software for the Wemos D1 mini. 310 | 311 | The *Makefile* is optional and can be useful only in the Linux 312 | environment and only using the *arduino-cli*, the command line based 313 | Arduino environment. 314 | 315 | Assuming that the *Wemos D1 mini* is connected, through the serial 316 | interface, to the RS41 and that it is powerd by the RS41 it will do 317 | the following steps: 318 | 319 | 1. Just after booting read the parameters stored on the EEPROM that 320 | are: 321 | 322 | - the frequency to setup using the TX registers, the values of the 323 | registers are calculated; 324 | - the time after boot, in secondos, that will be used to go to deep 325 | sleep mode to save power; 326 | - the SSID of the Access Point to connect to; 327 | - the password associated to the SSID; 328 | 329 | on first boot or if these parameters are not already stored on the 330 | EEPROM, it will select the default frequency 433.500 Mhz, 180 331 | seconds (3 minutes) as the time to deep sleep and a random SSID and 332 | password, so it will later fail to associate to an existing Access 333 | Point. 334 | 335 | 2. It will then send the "magic string" *([Enter]STwsv[Enter])* 336 | through the serial interface to the RS41 to enter the Service Menu. 337 | 338 | 3. It will input the register values, through the *(T)X registers* 339 | menu, to select the TX frequency. The value of these registers are 340 | automatically calculated. 341 | 342 | 4. It tries to connect to the Acces Point using the SSID stored in the 343 | EEPROM, if this association fails (for whatever reason like no SSID 344 | stored on EEPROM, invalid password or SSID not available etc.), it 345 | will start in Access Point mode advertising his own SSID that is 346 | *"esp8266_rs41_SETUP"*. 347 | 348 | 5. It then start a web server presenting a web form to select the 349 | frequency, that will be used at next reboot, to select the time to 350 | go to deep sleep mode and a link to another form to set the SSID 351 | and password to associate to, on next boot. 352 | 353 | 6. It will start a telnet server that is *bridged* to the serial port 354 | connected to the RS41, in this way it is possible to access the 355 | Service Menu through a simple telnet connection and without 356 | attaching more cables to the RS41 connector. 357 | 358 | ## Connecting the Wemos D1 mini to the RS41 connector 359 | 360 | ![Wemos D1 mini to RS41 connector](./img/rs41-wemos.png) 361 | 362 | The *Wemos D1 mini* uses the 363 | [ME6211 Voltage Regulator](https://datasheet.lcsc.com/szlcsc/Nanjing-Micro-One-Elec-ME6211A18M3G-N_C236677.pdf) 364 | that has a wide operating voltage range, for this reason it is safe to 365 | connect the *D1 mini* 5V input to the *Vcc_bust* pin of the RS41 366 | connector that is about 3.7V. We cannot use the RS41 Vcc pin because 367 | it is always at about 3.3V, also when the RS41 is powered off. The 368 | *Vcc_bust* pin, instead, is at 0V when the RS41 is powered off. 369 | 370 | The other pin connections are quite straightforward: 371 | 372 | * D1 Mini ground to the RS41 connector ground 373 | * D1 Mini D1 pin (software serial TX) to the UART_RX pin of the RS41 connector 374 | * D1 Mini D2 pin (software serial RX) to the UART_TX pin of the RS41 connector 375 | 376 | ## Prerequisites to compile the software 377 | 378 | To compile this software it is needed to setup the Arduino environment 379 | to support the ESP8266 chip and the Wemos D1 Mini, there are a lot of 380 | tutorials available on the web on how to do this, if not already done. 381 | 382 | Some additional libraries are needed to: 383 | 384 | - support WiFi connections as station and as Access Point 385 | - support On the Air firmware updates 386 | - implement the software serial interface on Wemos D1 mini, that has 387 | only serial port, the one used by the USB interface, to use a second 388 | serial port it is needed a software implementation that has limits 389 | but that, at 9600 baud, runs reasonably well; 390 | - implement a simple interface to read/write few parameters in EEPROM 391 | 392 | The complete list of libraries is the following, usually a version 393 | higher than the one listed is ok: 394 | 395 | ``` 396 | Library Name Version Author Architecture Type 397 | ----------------------------------- ---------- ------------------------- --------------- ------- 398 | ArduinoOTA 1.0 Ivan Grokhotkov and Migue esp8266 system 399 | DNSServer 1.1.1 Kristijan Novoselic esp8266 system 400 | EEPROM 1.0 Ivan Grokhotkov esp8266 system 401 | ESP8266WebServer 1.0 Ivan Grokhotkov esp8266 system 402 | ESP8266WiFi 1.0 Ivan Grokhotkov esp8266 system 403 | ESP8266mDNS 1.2 multiple, see files esp8266 system 404 | EspSoftwareSerial 6.8.5 Peter Lerup, Dirk Kaar esp8266,esp32 system 405 | ``` 406 | 407 | ## Running the software 408 | 409 | Once the software has been compiled and loaded into the *Wemos D1 410 | Mini* and the *D1 mini* has been connected to the RS41 we can proceed 411 | to power up the RS41 pushing the *switch* button for a couple of 412 | seconds: 413 | 414 | - The RS41 will boot and will start transmitting on the original 415 | frequency (for example on 404.80 Mhz), it will transmit a couple of 416 | packets before the *D1 Mini* will be able to change the frequency; 417 | 418 | - The *D1 Mini* will boot, will power on the on board led and will set 419 | the frequency, using the *(T)X registers* service menu; 420 | 421 | - The *D1 Mini* will power off the on board led, will start the WiFi, 422 | connecting to an Access Point or, if it fails, starting his own 423 | Access Point with the SSID *"esp8266_rs41_SETUP"*; 424 | 425 | - The *D1 Mini* will start a web server and a telnet daemon; 426 | 427 | - If the frequency setup on the RS41 was successfull the *D1 Mini* 428 | will start flashing the on board led with a slow speed (1 second on, 429 | 1 second off), if the frequency setup failed, the *D1 Mini* will 430 | flash the on board led very fast (a couple of on/off per seconds); 431 | in this way, if the onboard led is visible, it is possible to 432 | understand if the frequency setup was successfull or not. 433 | 434 | ### Connecting to the Wemos D1 Mini 435 | 436 | After the initial boot, the D1 Mini doesn't have anything stored on 437 | the EEPROM, so it is unable to connect to an existing access point and 438 | starts his own access point with the SSID *"esp8266_rs41_SETUP"* and 439 | IP address 192.168.1.1. 440 | 441 | It is possible to connect to this SSID and use a web browser to set 442 | the frequency, the time to deep sleep and the SSID/password of an 443 | access point to connect to as shown in the following images: 444 | 445 | ![WiFi list](./img/wifilist.png) 446 | 447 | In the following image the SSID *wifi-demo* with the password 448 | *demopass* has been selected: 449 | 450 | ![Set the SSID](./img/wifimainpage.jpg) 451 | 452 | It is important to understand security implications of these setup: 453 | 454 | * the SSID password travels in clear between the browser and the Wemos 455 | D1 mini; 456 | * the SSID password is stored in the Wemos D1 Mini EEPROM and it is 457 | sent from the D1 Mini to the browser in clear; 458 | * having physical access to the D1 mini is very easy to get the SSID 459 | password. 460 | 461 | ### The Wemos D1 Mini connected to the Access Point 462 | 463 | Once the D1 Mini has the SSID and related password stored in his 464 | EEPROM, it will connect to the Access Point as a client station. 465 | 466 | To connect to the D1 Mini web server or telnet server, from other 467 | devices on the same WiFi/LAN network, like a laptop or a mobile 468 | device, it is necessary to know his IP address. There are multiple way 469 | to find this IP address: 470 | 471 | * looking at information on the DHCP server, usually running on the 472 | WiFi Router/Access point, searching the mac address of the D1 Mini 473 | to find his IP address; 474 | 475 | * on Linux, the simplest way is using the *avahi-browse* as in the 476 | following screenshot to find the IP address that is, in this case, 477 | 192.168.2.102: 478 | 479 | ``` 480 | $ avahi-browse _arduino._tcp --resolve --terminate 481 | + eth0 IPv4 esp8266-rs41-fcf8b3 _arduino._tcp local 482 | = eth0 IPv4 esp8266-rs41-fcf8b3 _arduino._tcp local 483 | hostname = [esp8266-rs41-fcf8b3.local] 484 | address = [192.168.2.102] 485 | port = [8266] 486 | txt = ["tcp_check=no" "ssh_upload=no" "board="ESP8266_WEMOS_D1MINI"" "auth_upload=no"] 487 | ``` 488 | 489 | * otherwise the simplest method is using the *Fing* application on the 490 | smartphone (available for Android and iOS) connected to the same 491 | WiFi network as in the following screenshot; the ethernet address 492 | manufacturer (*Espressif* in this case) and the hostname 493 | (*esp8266-rs41-fcf8b3* in this case) can be used to identify our 494 | device: 495 | 496 | ![Fing](./img/wififing.png) 497 | 498 | ### Using telnet to access the RS41 Service Menu 499 | 500 | A (sort of) telnetd daemon runs on the Wemos D1 Mini and will bridge 501 | the telnet connection to the serial port connected to the RS41, in 502 | this way it is possible to access, wirelessly, the RS41 service menu. 503 | 504 | The telnet client usually negotiate options with the telnetd server, 505 | but, in this case, we have a simple tcp server listening on port 23 506 | and not a full blown telnetd daemon, for this reason it is important 507 | to manually select the correct options in the telnet client. 508 | 509 | Using the Linux telnet client we have to use the option *"set crmod"* 510 | (When this mode is enabled, most carriage return characters received 511 | from the remote host will be mapped into a carriage return followed by 512 | a line feed) and *"mode character"* (Disable the TELNET LINEMODE 513 | option), as in the following screenshot: 514 | 515 | ``` 516 | $ telnet 192.168.2.102 517 | Trying 192.168.2.102... 518 | Connected to 192.168.2.102. 519 | Escape character is '^]'. 520 | ^] 521 | telnet> set crmod 522 | Will map carriage return on output. 523 | ^] 524 | telnet> mode character 525 | 526 | ## not visible: [Enter]STwsv[Enter] 527 | 528 | (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 529 | TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 530 | TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 531 | (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 532 | > 533 | ``` 534 | 535 | Similar options on PuTTY are the followings: 536 | 537 | ![PuTTY telnet options](./img/putty-telnet.png) 538 | 539 | # The Makefile 540 | 541 | The Makefile can be used only in the Linux environment where the 542 | *arduino-cli* (Arduino Command Line Interface) is installed. 543 | 544 | The Makefile usage is described in the 545 | [arduino-makefile](https://github.com/digiampietro/arduino-makefile) 546 | repository. 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | -------------------------------------------------------------------------------- /esp8266-rs41.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 by Valerio Di Giampietro 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include "OTAdual.h" 28 | #include 29 | #include 30 | 31 | #define SER2_TX D1 32 | #define SER2_RX D2 33 | #define BUFLEN 1024 34 | //how many clients should be able to telnet to this ESP8266 35 | #define MAX_SRV_CLIENTS 1 36 | #define TCP_PORT (23) // Choose any port you want 37 | 38 | #ifndef min 39 | #define min(x,y) ((x)<(y)?(x):(y)) 40 | #endif 41 | 42 | #define TXFREQ_DEFAULT 433.500 // defaul tx freq when no frequency has been saved on EEPROM 43 | #define GETSERIALTIMEOUT 1200 // time used to read from RS41 serial 2000 is OK 44 | #define SLOWTIME_CMD 800 // time to wait for each keypress to enter command mode, 2000 is OK 45 | #define SLOWTIME 300 // time to wait for each keypress to select menu entries and input data 300 is ok 46 | #define TIME2SLEEP 180 // default time from boot to go to deep sleep mode to save power. Wakeup on reset or power on 47 | //#define DEBUG // if defined print debugging information on serial port 48 | #define STS_ERROR 99 // status error 49 | #define STS_OK 100 // status OK 50 | 51 | 52 | // ------ Global variables 53 | WiFiServer tcpServer(TCP_PORT); 54 | WiFiClient tcpServerClients[MAX_SRV_CLIENTS]; 55 | ESP8266WebServer webServer(80); //Server on port 80 56 | SoftwareSerial Serial2(SER2_RX, SER2_TX); // RX, TX 57 | 58 | char buf[BUFLEN]; // used to store serial responses from RS-41 59 | unsigned int buftail; // point to the last char in buf 60 | unsigned int buflastline; // point to the last Carriage Return in buf 61 | unsigned int time2sleep; // time, in seconds, after witch the ESP8266 will go to sleep to save power 62 | int status = 0; 63 | float txfreq = 0; 64 | char reg75[3],reg76[3],reg77[3]; 65 | char eeprom_ssid[30], eeprom_password[30]; 66 | 67 | String urlDecode(String sin) { 68 | String s = sin; 69 | s.replace("%20", " "); 70 | s.replace("+", " "); 71 | s.replace("%21", "!"); 72 | s.replace("%22", "\""); 73 | s.replace("%23", "#"); 74 | s.replace("%24", "$"); 75 | s.replace("%25", "%"); 76 | s.replace("%26", "&"); 77 | s.replace("%27", "\'"); 78 | s.replace("%28", "("); 79 | s.replace("%29", ")"); 80 | s.replace("%30", "*"); 81 | s.replace("%31", "+"); 82 | s.replace("%2C", ","); 83 | s.replace("%2E", "."); 84 | s.replace("%2F", "/"); 85 | s.replace("%2C", ","); 86 | s.replace("%3A", ":"); 87 | s.replace("%3A", ";"); 88 | s.replace("%3C", "<"); 89 | s.replace("%3D", "="); 90 | s.replace("%3E", ">"); 91 | s.replace("%3F", "?"); 92 | s.replace("%40", "@"); 93 | s.replace("%5B", "["); 94 | s.replace("%5C", "\\"); 95 | s.replace("%5D", "]"); 96 | s.replace("%5E", "^"); 97 | s.replace("%5F", "-"); 98 | s.replace("%60", "`"); 99 | return s; 100 | } 101 | 102 | inline void bridge_serial() { 103 | int n0=0; 104 | int m0=0;; 105 | int n1=0; 106 | int m1=0; 107 | int min0=0; 108 | int min1=0; 109 | 110 | while (( ((n0=Serial.available()) > 0) and ((m0=Serial2.availableForWrite()) > 0 ) ) 111 | or ( ((n1=Serial2.available()) > 0) and ((m1=Serial.availableForWrite()) > 0 ) )) { 112 | 113 | if (n0 > 0) { 114 | min0=min(n0,m0); 115 | min0=min(min0,BUFLEN); 116 | if (min0 > 0) { 117 | Serial.readBytes(buf,min0); 118 | Serial2.write(buf,min0); 119 | } 120 | } 121 | 122 | if (n1 > 0) { 123 | min1=min(n1,m1); 124 | min1=min(min1,BUFLEN); 125 | if (min1 > 0) { 126 | Serial2.readBytes(buf,min1); 127 | Serial.write(buf,min1); 128 | } 129 | } 130 | n0=0; 131 | m0=0; 132 | n1=0; 133 | m1=0; 134 | } 135 | } 136 | 137 | inline void rs41_get_serial (int timeout) { 138 | // fill the buffer buf and set buftail 139 | // store miximum the last BUFLEN chars in the buf circular buffer 140 | int bytesAvail, bytesIn, i; 141 | unsigned int startmillis; 142 | 143 | buftail = 0; 144 | buflastline = 0; 145 | startmillis = millis(); 146 | 147 | #ifdef DEBUG 148 | Serial.println("entering rs41_get_serial"); 149 | #endif 150 | 151 | while ((millis() - startmillis) < timeout) { 152 | //check UART for data 153 | while ((bytesAvail = Serial2.available()) > 0) { 154 | if (buftail >= sizeof(buf)) { 155 | buftail = 0; 156 | } 157 | bytesIn = Serial2.readBytes(buf+buftail, min(sizeof(buf)-buftail, bytesAvail)); 158 | delay(0); 159 | buftail += bytesIn; 160 | // Serial.print("bytesAvail/bytesIn: "); 161 | // Serial.println(bytesAvail); 162 | // Serial.println(bytesIn); 163 | } 164 | if ((buftail > 0) and (buf[buftail-1] == '>')) { 165 | break; 166 | } 167 | } 168 | 169 | for (i=0; i < buftail; i++) { 170 | if (buf[i] == 13) { // 13: Carriage Return 171 | buf[i]=10; // 10: New Line 172 | buflastline=i; 173 | } 174 | } 175 | 176 | buf[buftail]=0; 177 | 178 | #ifdef DEBUG 179 | Serial.print("buftail: "); 180 | Serial.println(buftail); 181 | Serial.print("buflastline: "); 182 | Serial.println(buflastline); 183 | Serial.println("Serial data from rs41:"); 184 | #endif 185 | Serial.println("------------------------------------------------------------------"); 186 | Serial.write(buf,buftail); 187 | Serial.println(""); 188 | Serial.println("------------------------------------------------------------------"); 189 | } 190 | 191 | inline void rs41_slow_write (char *buf, int maxlen, int msdelay) { 192 | int c; 193 | int i = 0; 194 | 195 | Serial.print("-----> rs41_slow_write: "); 196 | Serial.println(buf); 197 | while ((buf[i] != 0) and (i < maxlen)) { 198 | delay(msdelay); 199 | #ifdef DEBUG 200 | Serial.print("."); 201 | #endif 202 | Serial2.write(buf[i]); 203 | i++; 204 | delay(0); 205 | } 206 | #ifdef DEBUIG 207 | Serial.println(""); 208 | #endif 209 | } 210 | 211 | inline bool rs41_enter_command_mode () { 212 | Serial.println("-----> Sending STwsv to enter command mode"); 213 | //rs41_slow_write("\rSTwsv\r",10,SLOWTIME_CMD); 214 | rs41_slow_write("\rST",10,SLOWTIME_CMD); 215 | rs41_slow_write("wsv\r",10,SLOWTIME); 216 | rs41_get_serial(GETSERIALTIMEOUT); 217 | if (buf[buflastline+1] == '>') { 218 | Serial.println("-----> Entered Command mode"); 219 | return(true); 220 | } else { 221 | return(false); 222 | } 223 | } 224 | 225 | inline bool rs41_exit_command_mode () { 226 | Serial.println("-----> Sending e to exit command mode"); 227 | rs41_slow_write("e",5,SLOWTIME); 228 | rs41_get_serial(GETSERIALTIMEOUT); 229 | if ((buftail == 2) && (buf[0] == 'e')) { 230 | return(true); 231 | } else { 232 | return(false); 233 | } 234 | } 235 | 236 | 237 | // (S)ensors Fre(q)uencies (P)arameters (A)lfa TX p(o)wer 238 | // TX (f)requency T(X) state (T)X registers TX contin(u)ous TX ran(d)om 239 | // TX (c)arrier (B)aud rate Ser(i)al no (R)ed LED info (N)o menu 240 | // (K)eep test mode S(W) version (M)easurements (L)aunch/Drop (E)xit 241 | // >t 242 | // Register number (00-7F) >76 243 | // Register value B0 >02 244 | // OK 245 | 246 | inline bool rs41_set_register(char *reg, char *val) { 247 | char *p; 248 | 249 | Serial.println("-----> Sending t command"); 250 | rs41_slow_write("t",5,SLOWTIME); 251 | rs41_get_serial(GETSERIALTIMEOUT); 252 | if (strncmp(buf+buflastline+1,"Register number (00-7F) >",25) == 0) { 253 | Serial.println("-----> Got register change prompt"); 254 | } else { 255 | Serial.println("-----> ERROR missing register change prompt"); 256 | p=buf+buflastline+1; 257 | Serial.print(":");Serial.print(p);Serial.print(":"); 258 | return(false); 259 | } 260 | 261 | Serial.println("-----> Sending register number"); 262 | rs41_slow_write(reg,3,SLOWTIME); 263 | rs41_slow_write("\r",3,SLOWTIME); 264 | rs41_get_serial(GETSERIALTIMEOUT); 265 | if ((strncmp(buf+buflastline+1,"Register value ",15) == 0) && (buf[buftail-1] == '>')) { 266 | Serial.println("-----> Got register enter value prompt"); 267 | } else { 268 | Serial.println("-----> ERROR missing register enter value prompt"); 269 | p=buf+buflastline+1; 270 | Serial.print(":");Serial.print(p);Serial.print(":"); 271 | return (false); 272 | } 273 | 274 | rs41_slow_write(val,3,SLOWTIME); 275 | rs41_slow_write("\r",3,SLOWTIME); 276 | rs41_get_serial(GETSERIALTIMEOUT); 277 | if ((buf[buflastline+1] == '>') && (buf[3] == 'O') && (buf[4] == 'K')) { 278 | Serial.println("-----> Got main menu prompt"); 279 | } else { 280 | Serial.println("-----> ERROR missing main menu prompt"); 281 | p=buf+buflastline+1; 282 | Serial.print(":");Serial.print(p);Serial.print(":"); 283 | return (false); 284 | } 285 | return(true); 286 | } 287 | 288 | inline void bridge_tcp () { 289 | uint8_t i; 290 | int bytesAvail, bytesIn; 291 | 292 | // bridge_serial(); 293 | // --------------------------------------------------------------------------- 294 | //check if there are any new clients 295 | if (tcpServer.hasClient()) { 296 | for (i = 0; i < MAX_SRV_CLIENTS; i++) { 297 | //find free/disconnected spot 298 | if (!tcpServerClients[i] || !tcpServerClients[i].connected()) { 299 | if (tcpServerClients[i]) tcpServerClients[i].stop(); 300 | tcpServerClients[i] = tcpServer.available(); 301 | Serial.print("New client: "); Serial.println(i); 302 | continue; 303 | } 304 | } 305 | //no free/disconnected spot so reject 306 | WiFiClient tcpServerClient = tcpServer.available(); 307 | tcpServerClient.stop(); 308 | } 309 | 310 | //check clients for data 311 | for (i = 0; i < MAX_SRV_CLIENTS; i++) { 312 | if (tcpServerClients[i] && tcpServerClients[i].connected()) { 313 | //get data from the telnet client and push it to the UART 314 | while ((bytesAvail = tcpServerClients[i].available()) > 0) { 315 | bytesIn = tcpServerClients[i].readBytes(buf, min(sizeof(buf), bytesAvail)); 316 | if (bytesIn > 0) { 317 | Serial2.write(buf, bytesIn); 318 | delay(0); 319 | } 320 | } 321 | } 322 | } 323 | 324 | //check UART for data 325 | while ((bytesAvail = Serial2.available()) > 0) { 326 | bytesIn = Serial2.readBytes(buf, min(sizeof(buf), bytesAvail)); 327 | if (bytesIn > 0) { 328 | //push UART data to all connected telnet clients 329 | for (i = 0; i < MAX_SRV_CLIENTS; i++) { 330 | if (tcpServerClients[i] && tcpServerClients[i].connected()) { 331 | tcpServerClients[i].write((uint8_t*)buf, bytesIn); 332 | delay(0); 333 | } 334 | } 335 | } 336 | } 337 | } 338 | 339 | void handleRoot() { // When URI / is requested, send a web page with a form 340 | String s; 341 | char buf[35]; 342 | sprintf(buf,"%7.3f",txfreq); 343 | 344 | s="" 345 | " " 346 | " " 347 | " RS41 Set Frequency" 348 | " " 349 | " " 350 | "

Select RS41 TX frequency

" 351 | "

Current frequency is "; 352 | s.concat(buf); 353 | s.concat(" Mhz

"); 354 | s.concat("

Current SSID to connecto to: "); 355 | s.concat(eeprom_ssid); 356 | s.concat("

"); 357 | 358 | // ----- Form for RS41 frequency and time2sleep 359 | s.concat("
" 360 | "
" 361 | "RS41 Set Frequency using registers" 362 | "
" 363 | "
" 364 | "
" 368 | "
"); 369 | 370 | s.concat("
" 371 | "
" 372 | "
" 377 | "
"); 378 | 379 | s.concat("
" 380 | "
" 381 | "
" 382 | "
"); 383 | 384 | // -------- end of form for RS41 frequency and time to sleep 385 | 386 | // -------- form for SSID and Password to connect to 387 | s.concat("
" 388 | "
" 389 | "Set or Change Wifi Parameters" 390 | "
" 391 | "
" 392 | "
" 395 | "
"); 396 | 397 | s.concat("
" 398 | "
" 399 | "
" 404 | "
"); 405 | 406 | s.concat("
" 407 | "
" 408 | "
" 409 | "
"); 410 | 411 | // -------- end of form for SSID and Password to connect to 412 | s.concat(" " 413 | " " 414 | "" 415 | ); 416 | 417 | webServer.send(200, "text/html",s); 418 | 419 | } 420 | 421 | void calculateRegisters(float f) { 422 | float fref=26.0 / 3.0; 423 | unsigned int hbsel; 424 | unsigned int fb; 425 | unsigned int fc; 426 | 427 | Serial.println("-----> calculateRegisters"); 428 | 429 | if (f >= 416.0) { 430 | hbsel = 1; 431 | } else { 432 | hbsel = 0; 433 | } 434 | 435 | fb = floor(f/(hbsel+1)*30.0/260-24); 436 | fc = round(64000.0*f/(fref*(hbsel+1)) - 64000.0*fb - 64000.0*24); 437 | Serial.print("frequency: ");Serial.println(f); 438 | Serial.print("hbsel: ");Serial.println(hbsel); 439 | Serial.print("fb: ");Serial.println(fb); 440 | Serial.print("fc: ");Serial.println(fc); 441 | 442 | sprintf(reg75,"%02X",fb + hbsel * 32); 443 | sprintf(reg76,"%02X",fc/256); 444 | sprintf(reg77,"%02X",fc % 256); 445 | 446 | Serial.print("reg75 ");Serial.println(reg75); 447 | Serial.print("reg76 ");Serial.println(reg76); 448 | Serial.print("reg77 ");Serial.println(reg77); 449 | } 450 | 451 | void handleForm() { 452 | float f; 453 | char sf[15]; 454 | 455 | String freq = webServer.arg("freq"); 456 | String t2s = webServer.arg("time2sleep"); 457 | time2sleep = t2s.toInt(); 458 | 459 | f = freq.toFloat(); 460 | txfreq = f; 461 | calculateRegisters(f); 462 | EEPROM.put(0,f); 463 | EEPROM.put(sizeof(f),time2sleep); 464 | EEPROM.commit(); 465 | 466 | sprintf(sf,"%9.3f",f); 467 | String s = "" 468 | "" 469 | "" 470 | "" 476 | 477 | "RS41 Set Frequency" 478 | "" 479 | "" 480 | "

Set Frequency using registers

" 481 | "

Current time2sleep value: "; 482 | s.concat(t2s); 483 | s.concat("

"); 484 | s.concat("" 485 | "
Frequency:"); 486 | s.concat(sf); 487 | s.concat("
Register 75"); 488 | s.concat(reg75); 489 | s.concat("
Register 76"); 490 | s.concat(reg76); 491 | s.concat("
Register 77"); 492 | s.concat(reg77); 493 | s.concat("
" 494 | "

The new frequency has been stored in EEPROM, it will be the default from next reboot

" 495 | "

Click here to change to this frequency now, without rebooting" 496 | " please be patient, it will take time

" 497 | "

" 498 | " Go Back

" 499 | "" 500 | ""); 501 | webServer.send(200, "text/html", s); //Send web page 502 | } 503 | 504 | void handleSetfreq () { 505 | char buf[15]; 506 | sprintf(buf,"%10.3f",txfreq); 507 | String s = "" 508 | "" 509 | "" 510 | "RS41 Set Frequency Now" 511 | "" 512 | "" 513 | "

RS41 Set Frequency Now

"; 514 | 515 | digitalWrite(LED_BUILTIN, LOW); 516 | 517 | if (rs41_enter_command_mode() && 518 | rs41_set_register("75",reg75) && 519 | rs41_set_register("76",reg76) && 520 | rs41_set_register("77",reg77) && 521 | rs41_exit_command_mode()) { 522 | 523 | Serial.println("=====> Successfully initialized the RS41 Registers"); 524 | status = STS_OK; 525 | s.concat("

Successfully set the frequency "); 526 | s.concat(buf); 527 | s.concat(" Mhz

"); 528 | } else { 529 | Serial.println("======> Error in initializing the RS41 Registers"); 530 | status = STS_ERROR; 531 | s.concat("

ERROR failed to set the frequency"); 532 | s.concat(buf); 533 | s.concat(" Mhz

"); 534 | } 535 | 536 | s.concat("

Go Back

"); 537 | s.concat(""); 538 | digitalWrite(LED_BUILTIN, HIGH); 539 | webServer.send(200, "text/html", s); //Send web page 540 | 541 | } 542 | 543 | void handleSetWifi() { 544 | 545 | int eaddr = sizeof(txfreq) + sizeof(time2sleep); 546 | String ssid = urlDecode(webServer.arg("ssid")); 547 | String ssidpass = urlDecode(webServer.arg("ssidpass")); 548 | 549 | ssid.toCharArray(eeprom_ssid, sizeof(eeprom_ssid)); 550 | ssidpass.toCharArray(eeprom_password, sizeof(eeprom_password)); 551 | 552 | EEPROM.put(eaddr,eeprom_ssid); eaddr+=sizeof(eeprom_ssid); 553 | EEPROM.put(eaddr,eeprom_password); 554 | EEPROM.commit(); 555 | 556 | String s = "" 557 | "" 558 | " " 559 | "" 565 | 566 | "WIFI Parameters" 567 | "" 568 | "" 569 | "

WIFI Parameters

" 570 | "

SSID: "; 571 | s.concat(eeprom_ssid); 572 | s.concat("

"); 573 | s.concat("

SSID password: "); 574 | s.concat(eeprom_password); 575 | s.concat("

"); 576 | s.concat("

This parameters have been written to the EEPROM

"); 577 | 578 | s.concat(" Go Back

" 579 | "" 580 | ""); 581 | webServer.send(200, "text/html", s); //Send web page 582 | } 583 | 584 | 585 | void setup() { 586 | int eaddr; //EEPROM address 587 | pinMode(LED_BUILTIN, OUTPUT); 588 | digitalWrite(LED_BUILTIN, LOW); 589 | 590 | // ----- Setup EEPROM size, we store txfreq, eeprom_ssid, eeprom_password 591 | EEPROM.begin(sizeof(txfreq) + sizeof(time2sleep) + sizeof(eeprom_ssid) + sizeof(eeprom_password)); 592 | 593 | Serial.begin(115200); 594 | Serial2.begin(9600); 595 | Serial.println(); 596 | Serial.println(); 597 | 598 | // ----- Read data from EEPROM (txfreq, time2sleep, eeprom_ssid, eeprom_password) 599 | eaddr = 0; 600 | EEPROM.get(eaddr, txfreq); eaddr += sizeof(txfreq); 601 | EEPROM.get(eaddr, time2sleep); eaddr += sizeof(time2sleep); 602 | EEPROM.get(eaddr, eeprom_ssid); eaddr += sizeof(eeprom_ssid); 603 | EEPROM.get(eaddr, eeprom_password); 604 | 605 | eeprom_ssid[sizeof(eeprom_ssid)-1]=0; //zero terminated string, if invalid eeprom data 606 | eeprom_password[sizeof(eeprom_password)-1]=0; //zero terminated string, if invalid eeprom data 607 | 608 | Serial.print("Read txfreq from EEPROM: "); Serial.println(txfreq); 609 | Serial.print("Read time2sleep from EEPROM: "); Serial.println(time2sleep); 610 | Serial.print("Read ssid from EEPROM: "); Serial.println(eeprom_ssid); 611 | Serial.print("Read password from EEPROM: "); Serial.println(eeprom_password); 612 | 613 | if ((txfreq < 208.0) or (txfreq > 831.0)) { 614 | Serial.print("Invalid txfreq value, setting to "); 615 | Serial.println(TXFREQ_DEFAULT); 616 | txfreq = TXFREQ_DEFAULT; 617 | } 618 | 619 | if ((time2sleep < 180) or (time2sleep > 10800)) { // from 3 min to 3 hours 620 | Serial.print("Invalid time2sleep value, setting to "); 621 | Serial.println(TIME2SLEEP); 622 | time2sleep = TIME2SLEEP; 623 | } 624 | 625 | calculateRegisters(txfreq); 626 | 627 | if (rs41_enter_command_mode() && 628 | rs41_set_register("75",reg75) && 629 | rs41_set_register("76",reg76) && 630 | rs41_set_register("77",reg77) && 631 | rs41_exit_command_mode()) { 632 | 633 | Serial.println("=====> Successfully initialized the RS41 Registers"); 634 | status = STS_OK; 635 | } else { 636 | Serial.println("======> Error in initializing the RS41 Registers"); 637 | status = STS_ERROR; 638 | } 639 | 640 | digitalWrite(LED_BUILTIN, HIGH); 641 | displayLog("Setting WiFi"); 642 | setupOTA("esp8266-rs41",eeprom_ssid,eeprom_password); 643 | 644 | // Start TCP listener on port TCP_PORT 645 | tcpServer.begin(); 646 | tcpServer.setNoDelay(true); 647 | Serial.print("Ready! Use 'telnet or nc "); 648 | if (getWifiMode() == WIFICLIENT) { 649 | Serial.print(WiFi.localIP()); 650 | } else { 651 | Serial.print(getAPip()); 652 | } 653 | Serial.print(' '); 654 | Serial.print(TCP_PORT); 655 | Serial.println("' to connect"); 656 | 657 | webServer.on("/", handleRoot); 658 | webServer.on("/form", handleForm); 659 | webServer.on("/setfreq", handleSetfreq); 660 | webServer.on("/setwifi", handleSetWifi); 661 | webServer.begin(); 662 | } 663 | 664 | void displayLog(char s[]) { 665 | Serial.println(s); 666 | } 667 | 668 | void loop() { 669 | static unsigned int lastmillis = 0; 670 | static uint8_t ledstatus = 0; 671 | int blinktime = 1000; 672 | ArduinoOTA.handle(); 673 | 674 | if (status == STS_ERROR) { 675 | blinktime = 50; 676 | } 677 | 678 | bridge_tcp(); 679 | webServer.handleClient(); 680 | if ((millis() - lastmillis) >= blinktime) { 681 | lastmillis=millis(); 682 | if (status == STS_ERROR) { 683 | if (ledstatus == 0) { 684 | digitalWrite(LED_BUILTIN, LOW); 685 | } else { 686 | digitalWrite(LED_BUILTIN, HIGH); 687 | } 688 | } else { 689 | digitalWrite(LED_BUILTIN, ledstatus & 1); 690 | } 691 | ledstatus ++; 692 | ledstatus = ledstatus & 3; 693 | } 694 | 695 | if (millis() > time2sleep * 1000) { 696 | digitalWrite(LED_BUILTIN, HIGH); // power off the led 697 | Serial.println("=====> GOING TO DEEP SLEEP MODE TO SAVE ENERGY - RESET TO WAKEUP"); 698 | delay(500); 699 | ESP.deepSleep(0); 700 | } 701 | 702 | } 703 | -------------------------------------------------------------------------------- /img/putty-serial-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/putty-serial-setup.png -------------------------------------------------------------------------------- /img/putty-telnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/putty-telnet.png -------------------------------------------------------------------------------- /img/rs41-connector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/rs41-connector.png -------------------------------------------------------------------------------- /img/rs41-wemos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/rs41-wemos.png -------------------------------------------------------------------------------- /img/rs41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/rs41.png -------------------------------------------------------------------------------- /img/wififing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/wififing.png -------------------------------------------------------------------------------- /img/wifilist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/wifilist.png -------------------------------------------------------------------------------- /img/wifimainpage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiampietro/esp8266-rs41/d58a8b8c58dd6d6839e65cf529bb2ffa97ecce0f/img/wifimainpage.jpg --------------------------------------------------------------------------------