Ordering parts and planning the build (7)

(I try to link to the products I used so you can find them more easily. If you purchase them from these links I may receive compensation from affiliate programs. I am not employed or influenced by the manufacturers or distributors.)

After spending a couple of weeks strategizing about the build and the tech to go into it my friends invited me and my wife to come visit them in Michigan for the 4th of July. Since we live in a big city there aren’t a lot of open spaces where you can perform a launch without having to worry about trees, power lines, and population, so I thought this would be a great chance to get in a few launches and collect some telemetry data that I can use to plan further iterations. The only problem: I had no rocket.

With only 10 days before we left on our trip I needed to order all of the parts, receive them, assemble them, and hope everything dries in time to be able to pack it all up for the road.

I had already been looking at Apogee for inspiration on body designs and what kinds of materials are commonly used. Normally I might stray from the more expensive specialized retailers and try to buy the raw materials from cheaper businesses, but with the time crunch I figured I could streamline the process by purchasing all parts that were designed to work together.

I settled on the Estes BT-60 (Body Tube #60, 41.6mm) for a few reasons. It was large enough to fit the camera payload and telemetry electronics without being so large it would be too heavy or experience too much aerodynamic drag. Apogee had a clear payload tube, fiberglass engine compartment, nose cones and other components in this size. It was also very close to the diameter that I had already used in my computer modeling, so I could easily adjust the digital models to be accurate to the real build without too much re-working.

In total, there are just over a dozen body components that I needed, and some other tools and supplies that I used to assemble it. Here’s what I went with:

I probably could have done well to order a fin jig like the one they sell on Apogee, and perhaps some vice grips as well, but I had a deadline and I was just going to go for it! Unfortunately USPS delayed my order by a couple of days, but I did receive everything just with enough time that some late nights made up the difference.

In my next post I will show you how I put it all together, as we get closer to our first launch day!

Radio Telemetry – Connecting it all together (5)

(I try to link to the products I used so you can find them more easily. If you purchase them from these links I may receive compensation from affiliate programs. I am not employed or influenced by the manufacturers or distributors.)

Now that we know we will be using the Arduino Nano 33 BLE Sense and (for the sake of demonstration) the HC-12 RF module, let’s go ahead and put the software and hardware together. I’ll try to go into as much detail here as possible, and cover many of the issues I had, but there’s a lot, so bear with me. For all of the schematics I use Fritzing and I currently still write all of my code in the free Arduino IDE.

Sending sensor data over serial

First we can start with just reading as many of the sensors as we’d like to from the Arduino Nano and writing their values to the USB connection’s serial port. I load this particular sketch quite frequently when I’m checking the functionality of the board or the computer’s telemetry software (which we will cover later). This isn’t some beautiful perfectly written code, but it works just fine.

// Start with the includes
#include <Arduino_LSM9DS1.h>
#include <Arduino_LPS22HB.h>
#include <Arduino_HTS221.h>
#include <Arduino_APDS9960.h>

// These variables will be used to store the sensor values on each cycle
float ax;
float ay;
float az;
float cx;
float cy;
float cz;
float gx;
float gy;
float gz;
float pressure;
float temperature;
float humidity;
float prox;

unsigned long timer = millis();                 // Delay Timer

const int redPin = 22;
const int greenPin = 23;
const int bluePin = 24;
boolean flash = false;
const int statusColor = redPin;
boolean err = false;


void setup() {
  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT);
  digitalWrite(redPin, LOW); // Red on while we initialize
  delay(500);
  Serial.begin(9600);

  // These conditional statements give me feedback on Serial and with the LED
  if (!IMU.begin()) {
    Serial.println("Error initializing IMU sensor!");
    err = true;
  }
  if (!BARO.begin()) {
    Serial.println("Error initializing BARO sensor!");
    err = true;
  }
  if (!HTS.begin()) {
    Serial.println("Error initializing HTS sensor!");
    err = true;
  }
  if (!APDS.begin()) {
    Serial.println("Error initializing APDS sensor!");
    err = true;
  }
  digitalWrite(greenPin, LOW);
  if (!err) {
    digitalWrite(redPin, HIGH);
  }
}

void loop() {
  flash = !flash; // Alternating the LED is useful for visually checking cycle time
  digitalWrite(greenPin, flash); // green
  if (err) {
    digitalWrite(redPin, flash);
  }

  // Now we will read all of the sensors and write their values to the variables from before
  IMU.readAcceleration(ax, ay, az);
  IMU.readMagneticField(cx, cy, cz);
  IMU.readGyroscope(gx, gy, gz);
  pressure = BARO.readPressure();
  temperature = HTS.readTemperature();
  humidity = HTS.readHumidity();

  // Now we will print out a line of text over serial with each valuee separated by spaces
  Serial.print(millis());
  Serial.print(" ");
  Serial.print(pressure);
  Serial.print(" ");
  Serial.print(temperature);
  Serial.print(" ");
  Serial.print(humidity);
  Serial.print(" ");
  Serial.print(ax);
  Serial.print(" ");
  Serial.print(ay);
  Serial.print(" ");
  Serial.print(az);
  Serial.print(" ");
  Serial.print(cx);
  Serial.print(" ");
  Serial.print(cy);
  Serial.print(" ");
  Serial.print(cz);
  Serial.print(" ");
  Serial.print(gx);
  Serial.print(" ");
  Serial.print(gy);
  Serial.print(" ");
  Serial.println(gz);
}

If you want to see the output from this, while you’re in the Arduino editor, you can select the appropriate port in Tools > Port, and then view the output with Tools > Serial Monitor or as a graph in Tools > Serial plotter. Keep in mind that serial plotter will automatically pick up on the available values, plot them, and resize the screen to fit them. Because of this, the millis() function that I am using to track the delta-time will cause the screen to resize rapidly as the value increases, relegating the other values to appearing like straight lines with minuscule deviations. If you want a better plot then just comment out the Serial.print(millis()); line.

Your output will come out looking something like this if you’re really agitating the IMU. You can do a moving average if you’d rather have smoother curves (like in this picture) and you can even apply some kind of coefficient to normalize the values (in this example the denominator used in the moving average calculation).

If you’re not able to see the output from the Serial.print() statements, make sure that the “baud” in the bottom left of Serial Monitor or Serial Plotter matches the number from the Serial.begin() line.

Serial Plotter showing IMU data from the Arduino

I was back and forth about normalizing the IMU data, but I ultimately decided against it for a couple of reasons. First, if you are trying to make decisions based on sensor data, there’s no reason to water down the readings. I will eventually need to add some logic in to make decisions based on values over time, etc., but normalization doesn’t solve this problem.

The second reason is because while normalization does smooth out the graph beautifully, it also makes the readings lag behind by a significant amount. Timeliness matters when making automated in-flight decisions, so it’s better to let the flight computer have the raw values and then figure out how to handle them on its own.

Making the Transmitter

When it comes to wiring the HC-12 to the Arduino that we will be using as the telemetry computer, it’s pretty straightforward. I know that there are some great things you can do to make the circuits safer and more reliable, but I already told you this is not the place for that. You can easily connect the two devices pin-for-pin and it will work just fine. Here’s a handy chart of the pin names for your reference moving forward:

The Arduino has two different serial channels on it, which we will take advantage of here. There is a separate Serial1 channel with its own hardware TX and RX pins. Since radios use serial communications, we can use these to connect to the HC-12, which really simplifies the configuration.

Wireless telemetry transmitter wiring diagram

First, you will want a 9v battery connector. This can be accomplished by connecting the negative terminal to the ground, and the positive terminal to the VIN pin. You can power the Arduino in various ways, but some of them will only result in the 3V3 power output working. Using a 9V battery on the VIN will power the 5V power output, which is the voltage you need for optimum performance of the HC-12.

Next, to connect the HC-12, there are five wires that connect to the Arduino:

  1. The first two are the GND (black) and VCC (red), which make a complete circuit so electricity can flow between the two devices, powering the HC-12.
  2. Then connect the SET pin (Orange) on the HC-12 to digital out #2 on the Arduino. This pin will be defined in the software, and will be used to put the HC-12 into configuration mode so we can set channel number, TX strength, etc. We won’t use this pin, but it’s good to wire it up in case we need it.
  3. The TXD pin on the HC-12 connects to the RX pin on the Arduino (green) and the RXD pin on the HC-12 connects to the TX pin on the Arduino (Yellow). The reason for the flip-flop is because the labelling refers to the function in the context of the local device. You want the data to flow from a transmitting pin to a receiving pin, otherwise it wouldn’t be read.

Finally we connect the antenna. The HC-12 comes with small coil antennas that work just fine for shorter range applications, but you can pick up a couple of high gain antennas for $12 that will greatly improve your signal strength and quality.

3dbi high-gain antennas from Amazon

Programming the Transmitter

Now that the transmitting device is connected, we need to update our Arduino sketch from before with the code needed to operate the radio. Remember when I said before that the Nano has a built-in hardware Serial1. Technically it’s as simple as changing the Serial.print() statements to Serial1.print(). I’ll show you some more features of the HC-12 in a moment, but here’s a functioning copy of the remote telemetry code:

// Start with the includes
#include <Arduino_LSM9DS1.h>
#include <Arduino_LPS22HB.h>
#include <Arduino_HTS221.h>
#include <Arduino_APDS9960.h>

// These variables will be used to store the sensor values on each cycle
float ax;
float ay;
float az;
float cx;
float cy;
float cz;
float gx;
float gy;
float gz;
float pressure;
float temperature;
float humidity;
float prox;

unsigned long timer = millis();                 // Delay Timer

const int redPin = 22;
const int greenPin = 23;
const int bluePin = 24;
boolean flash = false;
const int statusColor = redPin;
boolean err = false;


void setup() {
  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT);
  digitalWrite(redPin, LOW); // Red on while we initialize
  delay(500);
  Serial1.begin(9600);

  // These conditional statements give me feedback on Serial1 and with the LED
  if (!IMU.begin()) {
    Serial1.println("Error initializing IMU sensor!");
    err = true;
  }
  if (!BARO.begin()) {
    Serial1.println("Error initializing BARO sensor!");
    err = true;
  }
  if (!HTS.begin()) {
    Serial1.println("Error initializing HTS sensor!");
    err = true;
  }
  if (!APDS.begin()) {
    Serial1.println("Error initializing APDS sensor!");
    err = true;
  }
  digitalWrite(greenPin, LOW);
  if (!err) {
    digitalWrite(redPin, HIGH);
  }
}

void loop() {
  flash = !flash; // Alternating the LED is useful for visually checking cycle time
  digitalWrite(greenPin, flash); // green
  if (err) {
    digitalWrite(redPin, flash);
  }

  // Now we will read all of the sensors and write their values to the variables from before
  IMU.readAcceleration(ax, ay, az);
  IMU.readMagneticField(cx, cy, cz);
  IMU.readGyroscope(gx, gy, gz);
  pressure = BARO.readPressure();
  temperature = HTS.readTemperature();
  humidity = HTS.readHumidity();

  // Now we will print out a line of text over serial with each value separated by spaces
  Serial1.print(millis());
  Serial1.print(" ");
  Serial1.print(pressure);
  Serial1.print(" ");
  Serial1.print(temperature);
  Serial1.print(" ");
  Serial1.print(humidity);
  Serial1.print(" ");
  Serial1.print(ax);
  Serial1.print(" ");
  Serial1.print(ay);
  Serial1.print(" ");
  Serial1.print(az);
  Serial1.print(" ");
  Serial1.print(cx);
  Serial1.print(" ");
  Serial1.print(cy);
  Serial1.print(" ");
  Serial1.print(cz);
  Serial1.print(" ");
  Serial1.print(gx);
  Serial1.print(" ");
  Serial1.print(gy);
  Serial1.print(" ");
  Serial1.println(gz);
}

When I was experimenting I noticed that the status light was flashing slower and slower with each new feature I was adding. This was of course to be expected because each line of code causes the loop to take longer to process, but I was not anticipating it to be so noticeable. After some experimentation I found that the biggest culprit was reading the humidity sensor. Since this was not essential for telemetry, and was more for curiosity, I eventually removed the humidity sensor from the equation.

One huge realization I had during this process was that since the Arduino could not handle parallel tasks, if I want to use the Arduino as the flight controller I’m going to have to really strip the components down to the essentials. If I still want to monitor

Making the Receiver

Now it’s time to wire up the second Arduino (the Uno) so it can act as the data receiver, and relay the data to the computer over the USB connection. There are some pre-built 433MHz serial data receivers, but where’s the fun in that??

Wiring diagram for the Arduino Uno and HC-12 telemetry receiver

Since we will be using the USB connection from the computer, this one is much simpler, but we will also be limited in the amount of voltage we can supply. Using the 3.3V output that we are limited to from the computer won’t be as much of a problem since we won’t be transmitting from this device, just receiving. To connect the HC-12 is just like before with one minor variation for the serial ports.

  1. The first two are the GND (black) and VCC (red), which make a complete circuit so electricity can flow between the two devices, powering the HC-12.
  2. Then connect the SET pin (Orange) on the HC-12 to pin #4 on the Arduino. This pin will be defined in the software, and will be used to put the HC-12 into configuration mode so we can set channel number, TX strength, etc. Since this device will be connected to the computer, we will probably use the SET pin at some point.
  3. This time we will do something completely different with the TX and RX pins. The Arduino Uno has a library called Software Serial that will allow us to define a serial channel without having to use the onboard pins. Because of this, the TXD pin on the HC-12 connects to pin 5 on the Arduino (green) and the RXD pin on the HC-12 connects to pin 6 on the Arduino (Yellow).

Once again we connect the antenna the same way as before. I picked up a plastic Arduino Uno project box that just snaps around it, but I had to drill a hole in the plastic to be able to mount the antenna. All in all I was pretty satisfied with the outcome:

The receiver inside its project box

Programming the Receiver

The Arduino sketch for the receiver is pretty simple. The basic goal is to take the output from the HC-12 radio and reprint it to the USB Serial output so the computer can process it. The HC-12 is connected to ports 5 and 6 which will be assigned to SoftwareSerial with the statement SoftwareSerial HC12().

Since the receiver is connected to the computer, we are going to add some functionality that will allow us to configure it if we need to. There’s an amazing post by Mark Hughes that covers the HC-12 system in detail, including how to use the set pin. Practically all of the sketch for the receiver came from this post (I even left in the comments for you since he did such a great job), and I spent hours just messing around with the radios to understand them well enough to feel comfortable trusting them in my rocket.

#include <SoftwareSerial.h>

const byte HC12RxdPin = 6;                      // "RXD" Pin on HC12
const byte HC12TxdPin = 5;                      // "TXD" Pin on HC12
const byte HC12SetPin = 4;                      // "SET" Pin on HC12

unsigned long timer = millis();                 // Delay Timer

char SerialByteIn;                              // Temporary variable
char HC12ByteIn;                                // Temporary variable
String HC12ReadBuffer = "";                     // Read/Write Buffer 1 for HC12
String SerialReadBuffer = "";                   // Read/Write Buffer 2 for Serial
boolean SerialEnd = false;                      // Flag to indicate End of Serial String
boolean HC12End = false;                        // Flag to indiacte End of HC12 String
boolean commandMode = false;                    // Send AT commands

// Software Serial ports Rx and Tx are opposite the HC12 Rx and Tx
// Create Software Serial Port for HC12
SoftwareSerial HC12(HC12TxdPin, HC12RxdPin);

void setup() {

  HC12ReadBuffer.reserve(64);                   // Reserve 64 bytes for Serial message input
  SerialReadBuffer.reserve(64);                 // Reserve 64 bytes for HC12 message input

  pinMode(HC12SetPin, OUTPUT);                  // Output High for Transparent / Low for Command
  digitalWrite(HC12SetPin, HIGH);               // Enter Transparent mode
  delay(80);                                    // 80 ms delay before operation per datasheet
  Serial.begin(9600);                           // Open serial port to computer
  HC12.begin(9600);                             // Open software serial port to HC12
}

void loop() {

  while (HC12.available()) {                    // While Arduino's HC12 soft serial rx buffer has data
    HC12ByteIn = HC12.read();                   // Store each character from rx buffer in byteIn
    HC12ReadBuffer += char(HC12ByteIn);         // Write each character of byteIn to HC12ReadBuffer
    if (HC12ByteIn == '\n') {                   // At the end of the line
      HC12End = true;                           // Set HC12End flag to true
    }
  }

  while (Serial.available()) {                  // If Arduino's computer rx buffer has data
    SerialByteIn = Serial.read();               // Store each character in byteIn
    SerialReadBuffer += char(SerialByteIn);     // Write each character of byteIn to SerialReadBuffer
    if (SerialByteIn == '\n') {                 // Check to see if at the end of the line
      SerialEnd = true;                         // Set SerialEnd flag to indicate end of line
    }
  }

  if (SerialEnd) {                              // Check to see if SerialEnd flag is true

    if (SerialReadBuffer.startsWith("AT")) {    // Has a command been sent from local computer
      HC12.print(SerialReadBuffer);             // Send local command to remote HC12 before changing settings
      delay(100);                               //
      digitalWrite(HC12SetPin, LOW);            // Enter command mode
      delay(100);                               // Allow chip time to enter command mode
      Serial.print(SerialReadBuffer);           // Echo command to serial
      HC12.print(SerialReadBuffer);             // Send command to local HC12
      delay(500);                               // Wait 0.5s for a response
      digitalWrite(HC12SetPin, HIGH);           // Exit command / enter transparent mode
      delay(100);                               // Delay before proceeding
    } else {
      HC12.print(SerialReadBuffer);             // Transmit non-command message
    }
    SerialReadBuffer = "";                      // Clear SerialReadBuffer
    SerialEnd = false;                          // Reset serial end of line flag
  }

  if (HC12End) {                                // If HC12End flag is true
    if (HC12ReadBuffer.startsWith("AT")) {      // Check to see if a command is received from remote
      digitalWrite(HC12SetPin, LOW);            // Enter command mode
      delay(100);                               // Delay before sending command
      Serial.print(SerialReadBuffer);           // Echo command to serial.
      HC12.print(HC12ReadBuffer);               // Write command to local HC12
      delay(500);                               // Wait 0.5 s for reply
      digitalWrite(HC12SetPin, HIGH);           // Exit command / enter transparent mode
      delay(100);                               // Delay before proceeding
      HC12.println("Remote Command Executed");  // Acknowledge execution
    } else {
      Serial.print(HC12ReadBuffer);             // Send message to screen
    }
    HC12ReadBuffer = "";                        // Empty buffer
    HC12End = false;                            // Reset flag
  }
}

Note how the HC12SetPin is brought low in order to execute AT commands. This comes in really handy when troubleshooting, because you can then just send “AT” across the Serial Monitor, and it will tell you the status of the system. I’m not sure if all of the delays are really necessary, but I feel like he knows a lot more about this than I do, so I’ll just leave it as-is.

If you need to configure the radio on the onboard telemetry transmitter you can always repurposes the above sketch to work with the Nano. Just remember the pins are different, and there will be no SoftwareSerial.

If you want a reference guide for the HC-12 component you can check out the datasheet, which clearly describes all of the AT codes, functions and limitations of the system. As a general rule, you will be able to find datasheets for almost every component you will use in projects like this, including the individual components on the Arduino, such as the IMU. I’ll show you how to edit the drivers using information in the datasheet to get more functionality from your onboard SoC’s in a later post.

Introduction – Space Frogs (1)

(I try to link to the products I used so you can find them more easily. If you purchase them from these links I may receive compensation from affiliate programs. I am not employed or influenced by the manufacturers or distributors.)

I think just about everyone has taken up a new hobby during the COVID pandemic. It just so happens that I spent a fair portion of our time in “quarantine” playing Kerbal Space Program, which inspired me to rediscover my love of rocketry. It wouldn’t be nearly as interesting, though, without a pile of convoluted tech. As it stands, I’ve only built one such rocket, and it only carried a small, somewhat dysfunctional remote telemetry package. Now I will start with the second generation of what I’ll nickname the markdart, and I’m going to try my best to blog about it.

I’m not making any commitments about the quality or accuracy of this blog. I’m not an engineer, this is not an “instructable”, and you should assume nothing I write about is safe, well planned, accurate, or best-practice.

What I do hope you take away from this blog is an inspiration to explore science and engineering without fear.

Kerbal Space Program

What do frogs have to do with rocketry? It’s not about the ballistic flight of amphibians post-leap, or the aerodynamic drag of pear-shaped amphibian bodies. It’s about simulation, exploration and inspiration.

Kerbal Space Program (KSP) is a game that lets players build and fly rockets and space planes, while employing realistic-enough physics modeling that teaches physics and engineering concepts like aerodynamics and orbital mechanics. It’s a beast of a game, with infinite possibilities, and some really fun missions that push players to build toward planetary exploration and scientific discovery.

The best part about KSP is that while fun and engaging, it’s a powerful learning tool for all ages, since it lets you experiment without fear of the consequences of real-world failures. Rockets are expensive and fail hard, so it’s way easier to experiment on a computer than on a real launchpad.

https://www.reddit.com/user/Space_Scumbag/

After countless days spent on KSP imaginary rockets weren’t enough, though. It was time to build something real.