Skip to content

Commit

Permalink
implement initial payload firmware and modem emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
kelvinabrokwa committed Oct 25, 2017
1 parent fa6dffd commit 671b4d5
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 194 deletions.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ OBJECTS = $(SOURCES:.cpp=.o)
BINARY = blink.elf
HEX = blink.hex

all: $(HEX)
all: #$(HEX)

# compile .cpp files to .o
.cpp.o:
Expand All @@ -29,6 +29,11 @@ $(BINARY): $(OBJECTS)
$(HEX): $(BINARY)
$(OBJCOPY) $(HEXFLAGS) $< $@

# symlinks this repo to the Arduino libraries directory so that its
# code can be included in Arduino Sketches written in the IDE
develop:
ln -s `pwd` $(ARDUINO_LIB_DIR)/TribeSat

docker-build:
docker build -t firmware-builder .
docker run --rm -v `pwd`/:/firmware firmware-builder make
Expand Down
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,25 @@

Onboard software for TribeSat

## Comm setup
## Developing

The communications sketches in the `src/TribeSatSerialPayload` and `src/TribeSatSerialModem` are an initial version of how to do communciations.
Running `make develop` symlinks this repository to your Arduino libraries directory. Make sure to use the absolute path in the command below.

```sh
make develop ARDUINO_LIB_DIR=/Users/kelvin/Arduino/libraries
```

Now you can include the various components of the firmware in your Arduino code:

```C++
#include "Comms.h"
#include "Packet.h"
...
```

To set up hardware for developing and debugging, you can use 2 Arduinos; one acting as our payload and the other as the modem. One Uno pins 10 and 11 are cross-connected (10 to 11 for RX1/TX2, 11 to 10 for TX1/RX2) for serial communication. You can monitor the boards using the Arduino IDE serial monitor.

## Tools

The `/tools` directory contains an Arduino sketch `ModemEmulator` that makes turns an Arduino into an emulator of the onboard modem. It reads packets on serial and ACKs/NAKs them as appropriate.

To develop this further, get two Arduino Uno's where pins 10 and 11 are cross-connected (10 to 11 for RX1/TX2, 11 to 10 for TX1/RX2). We use SoftwareSerial to connect using screen /dev/ttyACM0 57600 on the host computer end. Any screen input on the Payload side is turned into TribeSat ICD packets (1 char per packet) and transmitted to the Modem side where the entire packet is printed. The Modem side sends an ACK or NAK depending on whether the packet preamble is indeed HEX 50 50 50.
5 changes: 5 additions & 0 deletions library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name=TribeSat
version=0.0.1
author=Kelvin Abrokwa
maintainer=Kelvin Abrokwa
category=Communication
17 changes: 17 additions & 0 deletions src/Comms.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
*
*/

#include <Arduino.h>
#include "Comms.h"
#include "Packet.h"

uint8_t PACKET_HEADER[3] = { 0x50, 0x50, 0x50 };
uint8_t ACK[3] = { 0xAA, 0x05, 0x00 };
uint8_t NAK[3] = { 0xAA, 0x05, 0xFF };

//int sendPacket(Packet* packet) {}

bool isACK(uint8_t* p) {
return p[2] == 0;
}
27 changes: 27 additions & 0 deletions src/Comms.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
*
*/

#ifndef COMMS_H
#define COMMS_H

#include <Arduino.h>
#include "Packet.h"

#define SEND_NUM_RETRIES 5

extern uint8_t PACKET_HEADER[3];
extern uint8_t ACK[3];
extern uint8_t NAK[3];

/**
*
*/
//int sendPacket(Packet* packet);

/**
*
*/
bool isACK(uint8_t* p);

#endif /* COMMS_H */
98 changes: 98 additions & 0 deletions src/Packet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
*
*/
#define DEBUG // TODO: REMOVE ME

#include <Arduino.h>
#include "Packet.h"

/**
* Constructor with no packet
*/
Packet::Packet() {
this->header = this->packet; // header points to the top of the packet
this->data = this->packet + PACKET_HEADER_SIZE; // data is offset PACKET_HEADER_SIZE bytes
}

/**
* Destructor
*/
Packet::~Packet() {}

#ifdef DEBUG
/**
* Prints the packet to Serial
*/
void Packet::print() {
Serial.print("[Header = ");
for (int i = 0; i < PACKET_HEADER_SIZE; i++) {
Serial.print(this->header[i], HEX);
Serial.print(" ");
}

Serial.print(", Data = ");
for (int i = 0; i < PACKET_DATA_SIZE; i++) {
Serial.print(this->data[i], HEX);
Serial.print(" ");
}

Serial.println("]");

// Parse packet data and output individual fields
// temperature data
int temp = 0;
temp |= this->data[1];
temp <<= 8;
temp |= this->data[0];
Serial.print("Temperature: ");
Serial.println(temp);
}
#endif /* DEBUG */

/**
*
*/
void Packet::setHeader(uint8_t* header) {
for (int i = 0; i < PACKET_HEADER_SIZE; i++)
this->header[i] = header[i];
}

/**
* Write to a specific byte in the packet data
*/
void Packet::setData(uint8_t byt, int index) {
this->data[index] = byt;
}

/**
* Copies the data
*/
void Packet::setData(uint8_t* data) {
for (int i = 0; i < PACKET_DATA_SIZE; i++)
this->data[i] = data[i];
}

/**
* Encode sensor data into the packet
*/
void Packet::setField(PacketField field, void* data) {
int temp;
switch (field) {
case Temperature:
temp = *((int*)data);
this->data[0] = temp & 0xff;
this->data[1] = (temp >> 8) & 0xff;
break;
#ifdef DEBUG
default:
Serial.println("PACKET: invalid packet field");
#endif /* DEBUG */
}
}

/**
* Returns a reference to the underlying array that is the packet
*/
uint8_t* Packet::toArray() {
return this->packet;
}
41 changes: 41 additions & 0 deletions src/Packet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
*
*/
#define DEBUG // TODO: REMOVE ME

#ifndef PACKET_H
#define PACKET_H

#include <Arduino.h>


#define ACK_SIZE 3
#define PACKET_HEADER_SIZE 3
#define PACKET_DATA_SIZE 35
#define PACKET_SIZE (PACKET_HEADER_SIZE + PACKET_DATA_SIZE)

enum PacketField {
Temperature,
};

/**
* Packet data structure
*/
class Packet {
public:
Packet();
~Packet();
void setHeader(uint8_t* header);
void setData(uint8_t byt, int index);
void setData(uint8_t* data);
void setField(PacketField field, void* data);
uint8_t* toArray();
#ifdef DEBUG
void print();
#endif /* DEBUG */
private:
uint8_t* header;
uint8_t* data;
uint8_t packet[PACKET_SIZE];
};
#endif /* PACKET_H */
148 changes: 148 additions & 0 deletions src/Payload/Payload.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* Tribesat firmware entrypoint
*/
#include <SoftwareSerial.h>
#include <Wire.h>
#include "SparkFunTMP102.h"
#include "Packet.h"
#include "Comms.h"

#define DEBUG

SoftwareSerial ModemSerial(10, 11); // RX, TX

uint8_t *ackOrNak = new uint8_t[ACK_SIZE];
Packet *packet = new Packet();

// For Adafruit TMP102
const int TEMP_ALERT_PIN = A3;
TMP102 tempSensor(0x48); // Initialize sensor at I2C address 0x48

/**
*
*/
int readTempSensor() {
tempSensor.wakeup(); // turn sensor on
int temperature = (int)tempSensor.readTempC(); // read temperature data
tempSensor.sleep(); // put sensor in sleep mode
return temperature;
}

/**
*
*/
int sendPacket(Packet* packet) {
int retryCount = SEND_NUM_RETRIES;

#ifdef DEBUG
Serial.print("sending packet: ");
packet->print();
#endif /* DEBUG */

ModemSerial.write(packet->toArray(), PACKET_SIZE);

// read ACK or NAK - wait for 3 bytes to be written
while (ModemSerial.available() < ACK_SIZE)
delay(100);

for (int i = 0; i < ACK_SIZE; i++)
ackOrNak[i] = ModemSerial.read();

while (!isACK(ackOrNak) && retryCount) {
ModemSerial.write(packet->toArray(), PACKET_SIZE);

// read ACK or NAK - wait for 3 bytes to be written
while (ModemSerial.available() < ACK_SIZE)
delay(100);

for (int i = 0; i < ACK_SIZE; i++)
ackOrNak[i] = ModemSerial.read();

retryCount--;
}

if (isACK(ackOrNak)) {
return 1;
} else {
#ifdef DEBUG
Serial.print("Failed to send packet after "); Serial.print(SEND_NUM_RETRIES); Serial.println(" attemps");
#endif /* DEBUG */
return 0;
}
}

/**
* set up TMP102
*/
void initTempSensor() {
pinMode(TEMP_ALERT_PIN, INPUT);
tempSensor.begin(); // Join I2C bus

tempSensor.setFault(0); // Trigger alarm immediately

// Initialize tempSensor settings
// These settings are saved in the sensor, even if it loses power

// set the number of consecutive faults before triggering alarm.
// 0-3: 0:1 fault, 1:2 faults, 2:4 faults, 3:6 faults.
tempSensor.setFault(0); // Trigger alarm immediately

// set the polarity of the Alarm. (0:Active LOW, 1:Active HIGH).
tempSensor.setAlertPolarity(1); // Active HIGH

// set the sensor in Comparator Mode (0) or Interrupt Mode (1).
tempSensor.setAlertMode(0); // Comparator Mode.

// set the Conversion Rate (how quickly the sensor gets a new reading)
// 0-3: 0:0.25Hz, 1:1Hz, 2:4Hz, 3:8Hz
tempSensor.setConversionRate(2);

// set Extended Mode.
// 0:12-bit Temperature(-55C to +128C) 1:13-bit Temperature(-55C to +150C)
tempSensor.setExtendedMode(0);

// set T_HIGH, the upper limit to trigger the alert on
tempSensor.setHighTempC(29.4); // set T_HIGH in C

// set T_LOW, the lower limit to shut turn off the alert
tempSensor.setLowTempC(26.67); // set T_LOW in C

#ifdef DEBUG
Serial.println("TEMP: Temperature sensor setup");
#endif /* DEBUG */
}

/**
*
*/
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(57600);

// set the data rate for the SoftwareSerial port
ModemSerial.begin(38400);

// all outgoing packets have the same header - write this once during setup
packet->setHeader(PACKET_HEADER);

// set up temperature sensor
initTempSensor();
}

/**
*
*/
void loop() {
// temperature
int temp = readTempSensor();
packet->setField(Temperature, &temp);
int success = sendPacket(packet);

#ifdef DEBUG
if (success) {
Serial.println("Successfully sent packet");
} else {
Serial.println("Failed to send packet");
}
#endif
}
Loading

0 comments on commit 671b4d5

Please sign in to comment.