Preface

If you don’t know electronics, arduino, and a bit of computery stuff, I try my best to make it simple to everyoneas this article may be a little difficult to understand it’s ok to skip the mind boggling part. With that out of the way, get your favourite snack, and keep scrolling.

Table of contents

  1. Summary of previous log
  2. Live morse
  3. Reasons for me doing this
  4. So far so what
  5. Encryption
  6. Optimization
  7. Future optimization
  8. Some other things I have tried
    8.1. Vibration
    8.2. NodeMCU
    8.3. Bluetooth
  9. The code
    9.1. Sender
    9.2. Receiver
  10. You skipped the code part
  11. TL;DR
  12. Conclusion
  13. Comments

Summary of previous log.

In the previous log I talked about the initiation and the overall idea of the project. As I complete more of the features of the ARP(Arduino Radio Pager), I will update the checklist in the first post here. If you have not read it, It is strongly advised that you do so.

Live morse

In ye olden times, most of the morse systems were made up of sending series of long and short pulses over radiowaves. It ws all analog meanning that they were using good old simple electronics that had simple logic. And the advantage of that was that communications could be done over very long distances. It was used in boats a lot too. The machines were complex often needing someone who is skilled enough to tune and operate the radio but the overall way it worked was rather straightforwards. There was a pretty bad problem with it though. Anyone tuned in on the same frequency will be able to listen to whatever is going on in the communication. And that led to spies exfiltrating information. Having privacy was a bit overlooked at the time. The ARP project aims to fix that issue.
Now, I am aware that there are existing modern radio systems that are also encrypted that already do the same thing. And some might say “Why don’t you just use this, or that” blah blah blah. If you are thinking that way, Get out of here. 🚪 Ok, with that out of the way, lets continue.

Reasons for me doing this

Lots of technologies out there are licensed under restrictive licenses that don’t allow people to do what they want with products that they bought and are using. Doing so may violate the license and put them in trouble with the law. So I want to implement some sort of protocol from the ground up while using some help from already existing projects that don’t have restrictive licenses of course. A good example would be the RadioHead library which is licensed under the GPLv3.

So far so what???

Instead of using analog morse which will be able to be deciphered by anyone, I will be implementing that into a digital radio signal over RF433. But that is also not enough. Anyone who knows the Bitrate, frequency and timings of the radios used can decipher the signal. Infact here they are:

speed = 3800 Bits/second
Interval between packets = 1ms
Freq = 433MHz

To safely send morse over radio and not have people in between understand what’s going on, there will have to be a mutial understanding that is done beforehand prior to any sort of communication. We are going to share a keypair of sorts where only the 2 devices that are communicating can understand each other and no one/thing else.
Here is how this is gonna go down.

Encryption

For encrypting morse, I will not be using any complicated sort of encryption just yet. No AES or SHA or anything. That will come later for the Texting Functionality as the RadioHead library already has an encryption feature.
Each pair of devices will have the same array stored in memory and it will hold a number of random characters that will signify a certain pulse length. For example:

array_short = {a, e, v, d, w}
array_long = {b, u, t, c, s}
array_silent = {i, x, g, #, 2}

Now this is simply an example, in the real scenario, the arrays will be much larger. Depending on what is being sent, the devices will check the received value to the corresponding value in the array. Notice that there is an array for silence also. This is because instead of just sending a signal when we need to, we send a constant signal because having the signal turn on only when we are sending morse and trying to be stealthy about it will defeat the whole purpose so that’s why we also need to send a signal when we do not intend to send anything. And anyone listenning in between will simply see some random characters that don’t mean anything.
Ok Enough talking about encryption for now. Let’s move on to the fun stuff.

Optimization

So I decided to plug in an LED at the data lines of the transmitter and reciever which helped a lot in tuning it and getting the best range possible with my current setup. The antenna is pretty crude and could be a lot better. But it works well enough for the testing. About 30 meters.

I feel kinda dumb after doing so and here’s why.

When I observed the LED closely while I was pressing a button to transmit, The interval at which the LED was flikering seemed to be larger than when I’m transmitting the “Slience” signal. And when I found out what it was, I felt like jumping off the roof of my house. It was the OLED screen refreshing that was slowing it down. So I promptly disabled that part of the code and HOLY MACARONI! It worked better than it did before. No more delays. I did not know that updating the display was so demanding. update_display(customKey); –> //update_display(customKey);
So outputting anything on the oled screen while transmitting is a nono because of the limited resource of the arduino. Here is a visual. THe blinking of the LED should actually not happen and It should be a perceived as a continious light.

While refreshing the display
While not refreshing the display
Please note that the display I’m talking about here is the transmitter’s display and not the one seen in the right of the 2 shots above.
Pay more attention to the lime-LED. They are connected to the data lines of the radios and help visually see the pulses of data coming and going.

Future optimization

As of now, I am sending strings of text even in the morse functionality. So I have to find a way to shorten the packets lengths that are being sent. 1. to improve latency, 2. To not get laughed at from other programmers and 3. to reduce memory consumption of the arduino which will help in maintaining good battery life.
In the live morse, there is a whole byte as well as padding data being sent per packet. There should be a way for me to use nibbles(4 bits) instead of bytes(8-bits) and shorten the padding. That will require me to closely study the radiohead library.
All this talk about nibbles and byted makes me hungry…

Some other things I have tried.

Vibration

So I was planning to use a vibration motor to output the morse signal as it is meant to be a stalth device as we don’t want everyone who knows morse know what we are talking about. So, I got myself a pair of vibration-motor modules like these.

Vibration Motors
But there was a very big problem.
As i was trying to use them, I found out that after the vibration motor would stop spinning, The signal would deteriorate for a second or 2, And that is not acceptable as it is very important that signal is maintained in a real-time communication. The reasom was that the motor when it was spinning down, was generating an electromagnetic field which was then induced into the antenna. This causes the radio to become confused for a moment and the digital signal gets messy. So, I’ll need to get a smaller motor. (that was my intention from the beginning, but the shop did not have smaller motors.)

NodeMCU

I wanted to try using an ESP8266 that had built-in wifi to accomplish the same abstract task but the only devkit I could buy was a chinese clone that uses a different serial chip compared to the original or better clones.

ESP8266 Devkit
Why the NodeMCU you may ask. Mainly because it has a larger memory and is more powerful than the arduino pro-micro and I simply wanted to test compatibility as not everyone will have a pro-micro I want this project to work on as many microcontrollers as possible. secondly, I wanted to try to see if I could get the built-in wifi to talk to another ModeMCU. The main problem I had was that the serial interface was not working properly under linux. On my main pc the port would not even show up to upload the sketch to the board and on my laptop, I had to either enable regular users to access the port /dev/ttyUSB0 or just run the arduino IDE as root. The latter worked but If you’ve use linux for a while, you’ll know that running regular applications as root all the time can be dangerous.

For now, I’m still using the ardiono pro-micros and will worry about porting the code to other boards later.

Bluetooth

HC-06(left) and 05(right) modules
Fek bluetooth, It’s BS tachnology avoid using it as much as possible. It’s propriatery.

The code

Skip

Sender

#include <RH_ASK.h>
#include <SPI.h> // Not actually used but needed to compile

RH_ASK driver;

//   oled stuff

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width,  in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// declare an SSD1306 display object connected to I2C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Buttons
const int buttonPin = 10;     // the number of the pushbutton pin
int buttonState = 0;

// Keypad
#include <Keypad.h>

// Allow multitasking with mills.
unsigned long time = millis();


const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
};
byte rowPins[ROWS] = {9, 8, 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 5, 6}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);


void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
  }
  display.clearDisplay();
  update_display("BOOTING", 28, 20, 2, true);
  //display.display();
  delay(4000);
  //  Serial.begin(19200);    // Debugging only
  if (!driver.init())
    Serial.println("init failed");
// Put this on the display.
//  _     _           
// | |   (_)_   _____ 
// | |   | \ \ / / _ \
// | |___| |\ V /  __/
// |_____|_| \_/ \___|
  //display.dim(false);
  update_display(" _     _           \n| |   (_)_   _____ \n| |   | \\ \\ / / _ \\\n| |___| |\\ V /  __/\n|_____|_| \\_/ \\___|", 0, 0, 1, true);
  update_display("Morse", 14, 42, 3, false);

  //update_display("li");
}

int messageID = 0;
const int messagesSize = 7;
const char *messages[messagesSize] = {
  "-",
  ".",
  " ",
  "standby",
  "standby",
  "standby",
  "standby",
};

void loop() {
  char customKey = customKeypad.getKey();
  char customKeyState = customKeypad.getState();

  //const char *msg = "WubbaLubbaDubDub!";
//  driver.send((uint8_t *)messages[messageID], strlen(messages[messageID]));
//  driver.waitPacketSent();
//  delay(20);

//  if (millis() - time > 20)  //Has 20 millisecond passed?
//  {
//    //driver.send((uint8_t *)messages[messageID], strlen(messages[messageID]));
//    driver.send((uint8_t *)messages[messageID], strlen(messages[messageID]));
//    driver.waitPacketSent();
//    time = millis();           //and reset time.
//  }

  //  buttonState = digitalRead(buttonPin);
  if (customKeyState == PRESSED) {
    String prevChar = String(customKey);
    messageID = prevChar.toInt();
    //update_display("1", 0, 0, 8, true);
    //delay(200);
    //Serial.println(customKey);
    if (millis() - time > 1)  //Has 1 millisecond passed?
      {
        //driver.send((uint8_t *)messages[messageID], strlen(messages[messageID]));
        driver.send((uint8_t *)"a", 1);
        driver.waitPacketSent();
        time = millis();           //and reset time.
      }
  }else{
  	// messageID = 5;
  	if (millis() - time > 1)  //Has 1 millisecond passed?
  	  {
  	    //driver.send((uint8_t *)messages[messageID], strlen(messages[messageID]));
  	    driver.send((uint8_t *)"s", 1);
  	    driver.waitPacketSent();
  	    time = millis();           //and reset time.
  	  }
  }
  //  if (buttonState == HIGH && messageID < messagesSize-1) {
  //    // turn LED on:
  //    // Swap message
  //    messageID++;
  //
  //    // Update display
  //    update_display();
  //
  //    delay(200);
  //  } else if (buttonState == HIGH && messageID == messagesSize-1) {
  //    messageID = 0;
  //    update_display();
  //    delay(200);
  //  }
}

void update_display(String content, int x, int y, int size, bool repaint) {
  if(repaint){display.clearDisplay();}
  display.setCursor(x, y);
  display.setTextSize(size);
  display.setTextColor(WHITE, BLACK);     // text color
  display.println(content);
  display.display();
}

Receiver

// ask_receiver.pde
// -*- mode: C++ -*-
//  ____                            ___      ___  _     _____ ____  
// | __ ) _   _ ___________ _ __   ( _ )    / _ \| |   | ____|  _ \ 
// |  _ \| | | |_  /_  / _ \ '__|  / _ \/\ | | | | |   |  _| | | | |
// | |_) | |_| |/ / / /  __/ |    | (_>  < | |_| | |___| |___| |_| |
// |____/ \__,_/___/___\___|_|     \___/\/  \___/|_____|_____|____/ 

#include <RH_ASK.h>
#include <SPI.h> // Not actualy used but needed to compile

RH_ASK driver;

//   oled stuff

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width,  in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// declare an SSD1306 display object connected to I2C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Allow multitasking with mills.
unsigned long time = millis();

//  ____       _               
// / ___|  ___| |_ _   _ _ __  
// \___ \ / _ \ __| | | | '_ \ 
//  ___) |  __/ |_| |_| | |_) |
// |____/ \___|\__|\__,_| .__/ 
//                      |_|    
// 
void setup() {
  pinMode(8, OUTPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
  }
  //delay(5000);
  display.clearDisplay();
  display.setTextSize(8);
  for (int i = 0; i <= 5; i++) {
    display.clearDisplay();
    display.setCursor(44, 6);
    display.setTextColor(WHITE, BLACK);
    display.println(i);
    display.display();

    delay(1000);
  }
  //  Serial.begin(9600);	// Debugging only
  if (!driver.init())
    Serial.println("init failed");


  display.setTextSize(2);          // text size
  display.setCursor(0, 0);
  digitalWrite(8, HIGH);
  delay(100);
  digitalWrite(8, LOW);  
  display.clearDisplay();
}
// initialize OLED display with address 0x3C for 128x64

//GLOBALS
//unsigned long j = 0UL;
int pps = 0;
boolean hasConnection = false;

// _                      
//| |    ___   ___  _ __  
//| |   / _ \ / _ \| '_ \ 
//| |__| (_) | (_) | |_) |
//|_____\___/ \___/| .__/ 
//                 |_|
void loop(){
  uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];
  uint8_t buflen = sizeof(buf);
  if (driver.recv(buf, &buflen)) // Non-blocking
  {
    // At this point we know that we have a connection so lets keep that in mind
    hasConnection = true;
    //int i;
    
    //j++;
	pps++;
    // Message with a good checksum received, dump it.
    driver.printBuffer("Got:", buf, buflen);
    //Serial.print("Message: ");
    //Serial.println((char*)buf);
    
    display.setTextSize(2);

    display.setCursor(0, 0);
    display.setTextColor(WHITE, BLACK);     // text color
    String str = ((char*)buf);
    //display.println((char*)buf);
    display.fillRect(0, 0, 128, 46, BLACK);  
    display.println(str.substring(0, buflen));
    display.setTextSize(2);
    display.setCursor(0, 50);
    //reset packat count

    if (millis() - time > 1000){
	  display.print("N:"); display.print(pps); display.print(" ");
	  display.print("L:"); display.print(buflen); display.println("   ");
	  display.drawLine(0, 47, display.width() - 1, 47, WHITE);
	  //display.display();
      pps = 0;
	  time = millis();           //and reset time.
	}
  	
    if (str.substring(0, buflen) == "standby" || str.substring(0, buflen) == "s"){
      digitalWrite(8, LOW);
    }else{
      digitalWrite(8, HIGH);
    }
  display.display();
  }else{
    // If connection is lost, waut for 2 seconds before we can tell for sure that we are not connected anymore.
    if (millis() - time > 2000 && hasConnection == true){
      hasConnection = false;
      time = millis();           //and reset time.
    }
    // Show the user that there is no connection. Blinking. 
    if (!hasConnection){
      display.setTextSize(2);
      display.setCursor(0, 50);
      //display.clearDisplay();
      display.setCursor(0, 50);
      display.print("N:0        "); // Blank spaces to cover previous packet length.
      display.display();
      delay(500);
      display.setCursor(0, 50);
      display.print("           ");
      display.display();
      delay(500);
    } 
  }  
}

You skipped the code part

If you want to go back, here you go: sender receiver

TL;DR

Morse, works reliably now.
Updating the screen takes significant time.
Tried to put vibration motor, didn’t because of EMI.
Tried to use ESP8266, too much hassle, will try again later.
Didn’t even try to use Bluetooth.
Completely forgot that there was pizza in the fridge and didn’t eat lunch(True story).

Conclusion

It’s going good so far. I will be buying some smaller arduinos and radio modules and only implement this function on it. with no texting or whatever so that the morse function can have a standalone device if people want to just use that. And with the ones that have a screen, will have the live morse as well as all the other features listed in log-0.

POOT COMMENT DOWN PLEZ. I TIRED I GO SLEPT NOW 😴😴😴
Back to top


Comments