Walkie-Textie Wireless Communicator The Walkie-Textie is a simple handheld device with a 12-key keypad and OLED display that allows you to send and receive text messages using the LoRa wireless protocol. It's ideal for situations where there's no mobile signal, such as when you're camping or hiking in a remote area, when you don't want the cost of a mobile network, or for children to have fun without running up a bill: The Walkie-Textie is a handheld device that allows you to send text messages using the LoRa wireless protocol. The circuit uses a low-cost LoRa module, the HopeRF RFM95W, controlled by an ATtiny814, which also drives a low-cost monochrome OLED display to show the messages, reads the keyboard, provides an audio alert, and reads the battery level. Introduction The Walkie-Textie uses the LoRa wireless protocol which is unsuitable for speech, but ideal for short text messages. LoRa can reach a range of up to 3 miles (4.8 km) in urban areas, and up to 10 miles (16 km) in rural areas. It uses license-free sub-gigahertz radio frequency bands: 433 MHz or 868 MHz in Europe, 915 MHz in North and South America, 868 MHz in India, and 915 MHz in Asia. The LoRa modules are available in two versions: one for 433 MHz, and one that can be tuned to either 868 MHz or 915 MHz. Using the Walkie-Textie The Walkie-Textie has a twelve-key keypad, and like early mobile phones uses a technique called multi-tap to compose messages, in which repeatedly pressing the same key cycles through the characters for that key: The Walkie-Textie lets you write messages on a 12-key keypad using a technique called multi-tap. If you're too young to remember having to write messages like this count yourself lucky! But early texters became very fast at the technique. This is how it works: Press a key to enter one of the characters on that key. Pressing the key repeatedly steps through the letters and digit in sequence. To enter another character on the same key wait until the cursor reappears before pressing again. The '0' key includes space. The '1' key includes full stop and a sequence of punctuation marks and other symbols. To delete the last character press '#'. To send your message press '*'. When you receive a message the indicator light flashes and a beeper sounds. The Walkie-Textie limits the length of each message to one line of 20 characters, but you can send multiple lines in succession. When you receive replies they are interleaved with your messages in the scrolling window, in a similar way to message or chat apps. If you prefer not to have a beeper you can toggle it on or off by holding down the '#' key when you first turn on the Walkie-Textie. A status bar at the top of the display shows the signal strength of the last received message, the beeper status, and the battery level: History The Walkie-Textie is project that I started several months ago, with an initial prototype based on the Microchip RN2483 LoRa transmitter/receiver that you control via a serial interface using AT commands, but I found parsing the AT commands made writing the user interface very difficult, and the project ground to a halt. Recently I was experimenting with the HopeRF modules and decided that they would offer a simpler solution, and so resumed the project. The circuit Here's the circuit of the Walkie-Textie: The circuit of the Walkie-Textie, based on an RFM95W LoRa module and an ATtiny814. Although I used an ATtiny814 you could also use an ATtiny804, ATtiny1604, or ATtiny1614. The circuit takes advantage of my One Input Keypad Interface to read the keyboard using a single analogue input on the ATtiny814. The RFM95W is connected to four pins on the ATtiny814 to handle the SPI interface, and two additional pins to control RST and DIO0, the reset and interrupt pins. Because I'd run out of pins the circuit uses the same pin PA5 to drive the piezo beeper, and read the battery voltage via a voltage divider, since these functions don't interfere with each other. Display For the display I chose a 128x64 monochrome I2C OLED display available from AliExpress and eBay: These are based on the SH1106 driver chip, and the pins should be in the order GND, VCC, SCL, SDA. Note there are some virtually identical displays for sale with the order of the GND and VCC pins swapped, but these won't be suitable for the PCB without modification. The program is also compatible with an SSD1306 128x64 display by changing one constant. Aerial options The simplest antenna is just a short length of wire soldered to the connection marked ANT on the PCB. Alternatively the PCB will accommodate an SMA socket which will allow you to fit a 'rubber ducky' antenna. For more information see Providing an antenna below. Powering options The PCB has space for a 2-pin JST socket that will allow you to plug in a standard LiPo battery, such as the ones supplied by Adafruit [1]. I used a 240mAh 7 x 20 x 30mm LiPo cell. Alternatively you could power the Walkie-Textie from two AAA batteries; you can buy battery holders already fitted with a suitable JST plug [2]. A regulator drops the battery voltage to 3.3V suitable for the LoRa module. The typical current consumption is 24mA. Here's the full parts list (click to expand): Construction I designed a PCB in Eagle and sent it to JLCPCB for manufacture. For the solder mask colour I chose black because I thought it would be appropriate, making it look like a classic mobile phone. The components apart from the display and push buttons are mounted onto the back of the board: The components on the back of the Walkie-Textie PCB. The ATtiny814 is in an SOIC package, and the resistors, capacitor, and LED are all 0805 size, so they should be relatively easy to solder using a fine-tipped soldering iron. I recently bought a Miniware MHP50-B5 50x50mm hotplate [3] [4] and so I used this. Although the board doesn't fit on the hotplate, I did half of the board at a time by balancing it on the hotplate in the following sequence: Use the hotplate to solder the SMT components on the top half of the back of the board. Use the hotplate to solder the SMT push buttons on the bottom half of the front of the board. Finish the resistors on the bottom half of the back of the board, and the LED on the front of the board, with a hot air gun or soldering iron. Solder the switch using a soldering iron. Display The display header pin holes are staggered, with each hole shifted 8 mil (~0.2 mm) off-centre. This allows you to push the display's pin headers in place, and they will stay firmly connected without soldering. Back panel I used a spare PCB as a back panel, held in place with M3 screws and threaded pillars [5]. If you are using a small LiPo battery 8mm threaded pillars should be ideal, but for the 2 x AAA battery holder you'll need 18mm or 20mm threaded pillars. The upper front screws can be fitted with nuts or washers to hold the display in place too. Providing an antenna The PCB will allow you to fit an edge-mounting SMA connector, designed for a PCB with a thickness of 1.6mm, so you can use a matching "rubber ducky" antenna. Alternatively the antenna can simply be a short length of single-core cable connected to the centre terminal marked ANT on the back of the board. I tested both alternatives over 30 metres with one of my prototypes and the simple wire antenna gave similar RSSI readings to a rubber ducky, so unless you like the more professional look I recommend a wire antenna. The wire antenna should be a quarter-wave monopole, reduced by about 5% for the end effect; I used silicone stranded cable so it wouldn't break with repeated flexing [6]. The length for each frequency is as follows: Frequency Antenna length 433 MHz 165 mm 868 MHz 82 mm 915 MHz 78 mm The program Character terminal The Walkie-Textie uses my Tiny Graphics Library routines to interface to an SH1106 or SSD1306 monochrome I2C 128x64 OLED display, modified for use with the ATtiny814. These routines only support character-cell plotting, based on a display of 8 lines and 21 columns, and thus avoid the need for a memory display buffer. They also take advantage of the hardware vertical scroll provided by the SH1106/SSD1306 display drivers. The routines provided are: InitDisplay( ) - Display initialisation. ) - Display initialisation. ClearDisplay() - Clears the display. - Clears the display. ClearLine() - Clears a single line on the display. - Clears a single line on the display. ScrollDisplay() - Clears the top line, then scrolls the display up by one line. - Clears the top line, then scrolls the display up by one line. PlotChar() - Plots a character at a specified line and column using a 5x7 bitmap font. - Plots a character at a specified line and column using a 5x7 bitmap font. PrintString() - Prints a string to the display. Keypad Keypad uses my One Input Keypad Interface to read the entire keypad using a single analogue input on the ATtiny814. The multi-tap processing uses an array Multitap[] to define the sequence of characters assigned to each key: const char *Multitap[12] = { " 0", ".1!\"#$%&'()*+,-/:;<=>?@[\\]^_`{|}~", "ABC2abc", "DEF3def", "GHI4ghi", "JKL5jkl", "MNO6mno", "PQRS7pqrs", "TUV8tuv", "WXYZ9wxyz", "*", "#" }; I kept to the traditional assignment of letters to keys, but of course you can change this to suit your preferences. The multi-tap processing is handled in the main routine, Interface(). Within a timeout period of 1000 milliseconds pressing the same key again cycles through the sequence of letters assigned to the key, overprinting the previously entered character. Once the timeout period has expired the cursor is displayed by calling Display(ShowCursor), and the next keypress is entered at the next character position. LoRa interface The Walkie-Textie program uses of Sandeep Mistry's Arduino LoRa library [7] to provide the LoRa interface to the RFM95W. The LoRa interface uses the routine LoRa.onreceive() to register the function Receiver() as a callback, or Interrupt Service Routine. When a message is received this reads the message into the Received[] buffer, and sets the HaveReceived flag. The user interface is handled by the routine Interface(), called from the main Arduino loop() routine. This repeatedly calls ReadKeypad() to read the 12-key keypad, and waits for a keypress. While reading the keypad it checks the HaveReceived flag, and if so calls UpdateDisplayReceived() to scroll the screen and display the message on the left of the display. If a key has been pressed it processes it according to the multi-tap rules, calls Display() to display it, and appends it to the string Message[]. The special key '*' is handled differently; this calls LoRa.beginPacket() to send the message in Message[], and then UpdateDisplaySent() to scroll the display, and display the sent message on the right of the display. Both UpdateDisplayReceived() and UpdateDisplaySent() also call DrawStatus() to update the status bar at the top of the screen, which shows the RSSI of the last received message, the beeper status, and the battery level. Battery voltage The battery voltage is read by the analogue input Piezo using a voltage divider across the battery, to ensure that the battery voltage will always be within the 3.3V range of the analogue input. The analogue-to-digital converter uses a 2.5V internal voltage reference so that a reading can be obtained even when the voltage has dropped below the 3.3V regulator voltage. The following calculation returns the voltage in hundredths of a volt: int battery = analogRead(Piezo) * 21 / 43; where 21/43 is a good approximation to 2.5/1024 * 2 * 100. By default the battery display assumes a LiPo battery, with a maximum voltage of 4.2V, but you can specify other types of battery by uncommenting the appropriate line at the start of the source: int const FullBattery = 420; // LiPo battery // int const FullBattery = 320; // 2 x 1.5V Alkaline batteries eg Energizer Max // int const FullBattery = 360; // 2 x 1.5V Energizer Ultimate Lithium batteries Setup The main setup() routine initialises LoRa to specify the pins connected to the RFM95W board, and sets the following settings: LoRa.begin(868E6) - Change this to the appropriate value for the frequency of your LoRa module. - Change this to the appropriate value for the frequency of your LoRa module. LoRa.setSyncWord(SyncWord) - SyncWord should have the same value for all your units that need to intercommunicate. For example, if you have built a pair of Walkie-Texties for your neighbour's children, but you don't want them to interact with your children's Walkie-Texties, give them a different SyncWord. Compiling the program Compile the program using Spence Konde's megaTinyCore [8]. Choose the ATtiny3224/1624/1614/1604/824/814/804/424/414/404/214/204 option under the megaTinyCore heading on the Board menu. Check that the subsequent options are set as follows (ignore any other options): Chip: "ATtiny814" Clock: "20 MHz internal" To program the processor the recommended option is to use a 5V or 3.3V USB to Serial board, such as the SparkFun FTDI Basic board [9], or a USB to Serial cable [10], connected with a Schottky diode as follows. You can substitute a 4.7kΩ resistor for the Schottky diode: Connect the +5V/+3.3V, GND, and UPDI pins to the three terminals on the top edge of the Walkie-Textie. I held three pins in contact with the terminals to avoid the need to permanently solder a header to the board. If there's a battery fitted to the board leave the power switch turned off. Then proceed as follows: Set Programmer to "SerialUPDI - 230400 baud" . to . Select the USB port corresponding to the USB to Serial board in the Port menu. menu. Choose Upload from the Arduino IDE Tools menu to upload the program. Resources Here's the whole Walkie-Textie program: Walkie-Textie Program. Get the Eagle or Gerber files for the PCB here: https://github.com/technoblogy/walkie-textie. Or order a board from OSH Park here: Walkie-Textie Wireless Communicator. Further suggestions The LoRa radio can't receive when it's transmitting, and there's no handshaking, so the sender has no confirmation that their message was received. This can be solved manually by confirming receipt of each message. It would also be possible to automate this by sending a "message received" code after receiving each message. Also, all LoRa receivers within range will be capable of receiving a sent message. A simple level of privacy is provided by the SyncWord, which is a number from 1 to 255 that ensures that a receiver will only receive messages sent using the same SyncWord. For more privacy you could encrypt messages with a key known only to the sender and receiver. Updates 13th August 2025: Added a note about the typical current consumption. Please enable JavaScript to view the comments powered by Disqus. Disqus