Init
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/cmake-build-debug/
|
||||||
|
/cmake*
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
2
.idea/candash.iml
generated
Normal file
2
.idea/candash.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakePythonSetting">
|
||||||
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
|
</component>
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/candash.iml" filepath="$PROJECT_DIR$/.idea/candash.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.0)
|
||||||
|
project(candash C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
|
||||||
|
add_executable(candash main.c
|
||||||
|
base.c
|
||||||
|
base.h)
|
||||||
|
|
||||||
|
target_link_libraries(candash m ncurses)
|
88
base.c
Normal file
88
base.c
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 4/29/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint-gcc.h>
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
|
// Base64 decoding table
|
||||||
|
const unsigned char dtable[256] = {
|
||||||
|
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3,
|
||||||
|
['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7,
|
||||||
|
['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
|
||||||
|
['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
|
||||||
|
['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
|
||||||
|
['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
|
||||||
|
['Y'] = 24, ['Z'] = 25,
|
||||||
|
['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
|
||||||
|
['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33,
|
||||||
|
['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37,
|
||||||
|
['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
|
||||||
|
['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45,
|
||||||
|
['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49,
|
||||||
|
['y'] = 50, ['z'] = 51,
|
||||||
|
['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55,
|
||||||
|
['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
|
||||||
|
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int base64_decode(const char *in, unsigned char *out, size_t *out_len) {
|
||||||
|
size_t len = strlen(in);
|
||||||
|
size_t i, j;
|
||||||
|
unsigned char a, b, c, d;
|
||||||
|
|
||||||
|
if (len % 4 != 0) return -1;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < len; i += 4) {
|
||||||
|
a = dtable[(unsigned char)in[i]];
|
||||||
|
b = dtable[(unsigned char)in[i+1]];
|
||||||
|
c = dtable[(unsigned char)in[i+2]];
|
||||||
|
d = dtable[(unsigned char)in[i+3]];
|
||||||
|
|
||||||
|
out[j++] = (a << 2) | (b >> 4);
|
||||||
|
if (in[i+2] != '=') {
|
||||||
|
out[j++] = (b << 4) | (c >> 2);
|
||||||
|
if (in[i+3] != '=')
|
||||||
|
out[j++] = (c << 6) | d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out_len = j;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hex_dump(const unsigned char *data, size_t len) {
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
printf("%02X ", data[i]);
|
||||||
|
if ((i + 1) % 16 == 0)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t crc32_table[256];
|
||||||
|
|
||||||
|
// Call this once before computing CRCs
|
||||||
|
void init_crc32_table(void) {
|
||||||
|
uint32_t poly = 0xEDB88320; // reversed polynomial of 0x04C11DB7
|
||||||
|
for (uint32_t i = 0; i < 256; i++) {
|
||||||
|
uint32_t crc = i;
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
if (crc & 1)
|
||||||
|
crc = (crc >> 1) ^ poly;
|
||||||
|
else
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
crc32_table[i] = crc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t crc32_le(uint32_t crc, const uint8_t *buf, size_t len) {
|
||||||
|
crc = ~crc;
|
||||||
|
while (len--) {
|
||||||
|
crc = crc32_table[(crc ^ *buf++) & 0xFF] ^ (crc >> 8);
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
15
base.h
Normal file
15
base.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 4/29/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CANDASH_BASE_H
|
||||||
|
#define CANDASH_BASE_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void hex_dump(const unsigned char *data, size_t len);
|
||||||
|
int base64_decode(const char *in, unsigned char *out, size_t *out_len);
|
||||||
|
extern const unsigned char dtable[256];
|
||||||
|
void init_crc32_table(void);
|
||||||
|
uint32_t crc32_le(uint32_t crc, const uint8_t *buf, size_t len);
|
||||||
|
#endif //CANDASH_BASE_H
|
326
main.c
Normal file
326
main.c
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint-gcc.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "packets.h"
|
||||||
|
#include "base.h"
|
||||||
|
|
||||||
|
#define MAG_SCALE (4912.0f / 32760.0f)
|
||||||
|
|
||||||
|
void printTelemetryPacketNcurses(TelemetryPacket *packet, DownBoundPacket *down) {
|
||||||
|
clear();
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Time
|
||||||
|
attroff(COLOR_PAIR(6));
|
||||||
|
mvprintw(row++, 0, "Timestamp: %u", down->missionTimer);
|
||||||
|
|
||||||
|
// MPU9250
|
||||||
|
if (packet->presentDevices & MPU9250_PRESENT_BIT) {
|
||||||
|
attron(COLOR_PAIR(1));
|
||||||
|
mvprintw(row++, 0, "-- MPU9250 --");
|
||||||
|
} else {
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
mvprintw(row++, 0, "-- MPU9250 (not present) --");
|
||||||
|
}
|
||||||
|
float ax = packet->accelerationX / 16384.0f;
|
||||||
|
float ay = packet->accelerationY / 16384.0f;
|
||||||
|
float az = packet->accelerationZ / 16384.0f;
|
||||||
|
float gx = packet->gyroX / 131.0f;
|
||||||
|
float gy = packet->gyroY / 131.0f;
|
||||||
|
float gz = packet->gyroZ / 131.0f;
|
||||||
|
float mx = packet->magnetX * MAG_SCALE;
|
||||||
|
float my = packet->magnetY * MAG_SCALE;
|
||||||
|
float mz = packet->magnetZ * MAG_SCALE;
|
||||||
|
float temp = (packet->accelerometer_temperature / 333.87f) + 21.0f;
|
||||||
|
mvprintw(row++, 2, "Accel [g]: X=%.2f Y=%.2f Z=%.2f", ax, ay, az);
|
||||||
|
mvprintw(row++, 2, "Gyro [deg/s]: X=%.2f Y=%.2f Z=%.2f", gx, gy, gz);
|
||||||
|
mvprintw(row++, 2, "Magnet [uT]: X=%.2f Y=%.2f Z=%.2f", mx, my, mz);
|
||||||
|
mvprintw(row++, 2, "Temp [°C]: %.2f", temp);
|
||||||
|
attroff(COLOR_PAIR(1));
|
||||||
|
attroff(COLOR_PAIR(4));
|
||||||
|
|
||||||
|
// CCS811
|
||||||
|
if (packet->presentDevices & CCS811_PRESENT_BIT) {
|
||||||
|
attron(COLOR_PAIR(2));
|
||||||
|
mvprintw(row++, 0, "-- CCS811 --");
|
||||||
|
} else {
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
mvprintw(row++, 0, "-- CCS811 (not present) --");
|
||||||
|
}
|
||||||
|
mvprintw(row++, 2, "eCO2: %u ppm", packet->eCO2);
|
||||||
|
mvprintw(row++, 2, "TVOC: %u ppb", packet->tvoc);
|
||||||
|
mvprintw(row++, 2, "Current: %u", packet->currentCCS);
|
||||||
|
mvprintw(row++, 2, "Raw CCS: %u", packet->rawCCSData);
|
||||||
|
attroff(COLOR_PAIR(2));
|
||||||
|
attroff(COLOR_PAIR(4));
|
||||||
|
|
||||||
|
// INA260
|
||||||
|
if (packet->presentDevices & INA260_PRESENT_BIT) {
|
||||||
|
attron(COLOR_PAIR(3));
|
||||||
|
mvprintw(row++, 0, "-- INA260 --");
|
||||||
|
} else {
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
mvprintw(row++, 0, "-- INA260 (not present) --");
|
||||||
|
}
|
||||||
|
mvprintw(row++, 2, "Voltage [V]: %.4f", packet->volts * 0.00125);
|
||||||
|
mvprintw(row++, 2, "Current [A]: %.4f", packet->current * 0.00125);
|
||||||
|
mvprintw(row++, 2, "Power [W]: %.4f", packet->power * 0.01);
|
||||||
|
attroff(COLOR_PAIR(3));
|
||||||
|
attroff(COLOR_PAIR(4));
|
||||||
|
|
||||||
|
// BME680
|
||||||
|
if (packet->presentDevices & BME680_PRESENT_BIT) {
|
||||||
|
attron(COLOR_PAIR(5));
|
||||||
|
mvprintw(row++, 0, "-- BME680 --");
|
||||||
|
} else {
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
mvprintw(row++, 0, "-- BME680 (not present) --");
|
||||||
|
}
|
||||||
|
mvprintw(row++, 2, "Temp: %d - %.2f °C", packet->temperature, packet->air_temperature / 100.0f);
|
||||||
|
mvprintw(row++, 2, "Humidity: %d - %.2f %%", packet->humidity, packet->relative_humidity / 100.0f);
|
||||||
|
mvprintw(row++, 2, "Pressure: %d - %.2f hPa", packet->pressure, packet->barometric_pressure / 100.0f);
|
||||||
|
mvprintw(row++, 2, "Gas Resistance: %d - %.2f Ohm", packet->gas, packet->gas_resistance / 1000.0f);
|
||||||
|
mvprintw(row++, 2, "Gas Valid: %s", packet->gas_valid ? "Yes" : "No");
|
||||||
|
mvprintw(row++, 2, "Heater Stable: %s", packet->heater_stable ? "Yes" : "No");
|
||||||
|
|
||||||
|
mvprintw(row++, 2, "IAQ: %u", packet->iaq_score);
|
||||||
|
mvprintw(row++, 2, "TempScore: %.2f", packet->temperature_score);
|
||||||
|
mvprintw(row++, 2, "HumidityScore: %.2f", packet->humidity_score);
|
||||||
|
mvprintw(row++, 2, "GasScore: %.2f", packet->gas_score);
|
||||||
|
attroff(COLOR_PAIR(5));
|
||||||
|
attroff(COLOR_PAIR(4));
|
||||||
|
|
||||||
|
// GPS
|
||||||
|
attron(COLOR_PAIR(6));
|
||||||
|
mvprintw(row++, 0, "-- GPS --");
|
||||||
|
time_t rawtime = packet->time_seconds;
|
||||||
|
struct tm *ptm = gmtime(&rawtime);
|
||||||
|
|
||||||
|
// Parse custom date format YYDDMM -> YYYY-MM-DD
|
||||||
|
int date = packet->date_yyddmm;
|
||||||
|
int yy = date / 10000;
|
||||||
|
int dd = (date / 100) % 100;
|
||||||
|
int mm = date % 100;
|
||||||
|
int year = 2000 + yy;
|
||||||
|
|
||||||
|
mvprintw(row++, 2, "Time: %02d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
|
||||||
|
mvprintw(row++, 2, "Date: %04d-%02d-%02d", year, mm, dd);
|
||||||
|
mvprintw(row++, 2, "Lat: %.4f°", packet->latitude_centi_degrees / 10000.0f);
|
||||||
|
mvprintw(row++, 2, "Long: %.4f°", packet->longitude_centi_degrees / 10000.0f);
|
||||||
|
mvprintw(row++, 2, "Alt: %.2f m", packet->altitude_centi_meters / 100.0f);
|
||||||
|
mvprintw(row++, 2, "Speed: %.2f knots", packet->speed_centi_knots / 100.0f);
|
||||||
|
mvprintw(row++, 2, "Satellites: %u", packet->num_satellites);
|
||||||
|
mvprintw(row++, 2, "Fix Quality: %u", packet->fix_quality);
|
||||||
|
|
||||||
|
// GPS Prediction
|
||||||
|
mvprintw(row++, 2, "Predicted Lat: %.4f°", packet->predicted_latitude_centi_degrees / 10000.0f);
|
||||||
|
mvprintw(row++, 2, "Predicted Long: %.4f°", packet->predicted_longitude_centi_degrees / 10000.0f);
|
||||||
|
mvprintw(row++, 2, "Predicted Alt: %.2f m", packet->predicted_altitude_centi_meters / 100.0f);
|
||||||
|
|
||||||
|
// MCP23018 ADC
|
||||||
|
if (packet->presentDevices & MCP23018_PRESENT_BIT) {
|
||||||
|
attron(COLOR_PAIR(3));
|
||||||
|
mvprintw(row++, 0, "-- ADC sensors --");
|
||||||
|
} else {
|
||||||
|
attron(COLOR_PAIR(4));
|
||||||
|
mvprintw(row++, 0, "-- ADC sensors (not present) --");
|
||||||
|
}
|
||||||
|
mvprintw(row++, 2, "NH3: %d", packet->NH3);
|
||||||
|
mvprintw(row++, 2, "CO: %d", packet->CO);
|
||||||
|
mvprintw(row++, 2, "NO2: %d", packet->NO2);
|
||||||
|
mvprintw(row++, 2, "UVC: %d", packet->UVC);
|
||||||
|
|
||||||
|
// Servo
|
||||||
|
attron(COLOR_PAIR(5));
|
||||||
|
mvprintw(row++, 0, "-- Servos --");
|
||||||
|
mvprintw(row++, 2, "Servo A: Current=%d Target=%d", packet->currentServoA, packet->targetServoA);
|
||||||
|
mvprintw(row++, 2, "Servo B: Current=%d Target=%d", packet->currentServoB, packet->targetServoB);
|
||||||
|
|
||||||
|
// Index
|
||||||
|
mvprintw(row++, 0, "Telemetry Index: %d", packet->telemetryIndex);
|
||||||
|
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TelemetryPacket telemetryPacket;
|
||||||
|
DownBoundPacket down;
|
||||||
|
|
||||||
|
void handle_downlink_packet(uint8_t *buf, uint8_t rxLen) {
|
||||||
|
if (rxLen < sizeof(DownBoundPacket)) {
|
||||||
|
printf("Received packet too small to be valid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&down, buf, sizeof(DownBoundPacket));
|
||||||
|
|
||||||
|
printf("Downlink packet index: %u, type: %u", down.packetIndex, down.packetType);
|
||||||
|
|
||||||
|
// Verify sync phrase
|
||||||
|
if (strncmp(down.syncPhrase, DownlinkSync, strlen(DownlinkSync)) != 0) {
|
||||||
|
printf("Invalid sync phrase, ignoring packet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *payload = buf + sizeof(DownBoundPacket);
|
||||||
|
uint32_t payloadSize = rxLen - sizeof(DownBoundPacket);
|
||||||
|
|
||||||
|
uint32_t crcCheck = crc32_le(0, payload, payloadSize);
|
||||||
|
|
||||||
|
if (crcCheck != down.CRCCheck) {
|
||||||
|
printf("Received BAD CRC for packet %u, crc is %u, should be %u", down.packetIndex, crcCheck, down.CRCCheck);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (down.packetType) {
|
||||||
|
case DownlinkPacketType_Telemetry:
|
||||||
|
if (payloadSize >= sizeof(TelemetryPacket)) {
|
||||||
|
memcpy(&telemetryPacket, payload, sizeof(TelemetryPacket));
|
||||||
|
printf("Telemetry packet received!");
|
||||||
|
|
||||||
|
printTelemetryPacketNcurses(&telemetryPacket, &down);
|
||||||
|
// Here you would actually do something with the telemetry, like save it or display it
|
||||||
|
} else {
|
||||||
|
printf("Telemetry packet too small (%u bytes)", payloadSize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DownlinkPacketType_Ping:
|
||||||
|
printf("Ping packet received!");
|
||||||
|
// Optionally respond with pong here
|
||||||
|
break;
|
||||||
|
case DownlinkPacketType_ACK:
|
||||||
|
printf("ACK packet received!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Unknown packet type: %u", down.packetType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int open_serial(const char *device) {
|
||||||
|
int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct termios tty;
|
||||||
|
memset(&tty, 0, sizeof tty);
|
||||||
|
if (tcgetattr(fd, &tty) != 0) {
|
||||||
|
perror("tcgetattr");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfsetospeed(&tty, B115200);
|
||||||
|
cfsetispeed(&tty, B115200);
|
||||||
|
|
||||||
|
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
|
||||||
|
tty.c_iflag &= ~IGNBRK; // disable break processing
|
||||||
|
tty.c_lflag = 0; // no signaling chars, no echo, no canonical processing
|
||||||
|
tty.c_oflag = 0; // no remapping, no delays
|
||||||
|
tty.c_cc[VMIN] = 1; // read doesn't block
|
||||||
|
tty.c_cc[VTIME] = 1; // 0.1 seconds read timeout
|
||||||
|
|
||||||
|
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
|
||||||
|
|
||||||
|
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls, enable reading
|
||||||
|
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
|
||||||
|
tty.c_cflag &= ~CSTOPB;
|
||||||
|
tty.c_cflag &= ~CRTSCTS;
|
||||||
|
|
||||||
|
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||||
|
perror("tcsetattr");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int subMain() {
|
||||||
|
|
||||||
|
int fd = open_serial("/dev/ttyUSB0");
|
||||||
|
if (fd < 0) return 1;
|
||||||
|
|
||||||
|
char buf[4096];
|
||||||
|
size_t buf_pos = 0;
|
||||||
|
|
||||||
|
memset(&telemetryPacket, 0, sizeof(TelemetryPacket));
|
||||||
|
memset(&down, 0, sizeof(DownBoundPacket));
|
||||||
|
|
||||||
|
unsigned char decoded[256];
|
||||||
|
size_t decoded_len;
|
||||||
|
// if (base64_decode("UGxlY2hEb2xlADsAAAAADuo+Ai9is1eY/vw/dP4AAAAAAAAAAAAAAABgD8sBCAAB/wPGGEYARQCgHQgAoksEKgUAnwIBAQQAe5APQtL3pEGycXlEqDLaSDcAAAAAAJqZeUAAAFBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zw=", decoded, &decoded_len) == 0) {
|
||||||
|
// printf("Decoded data (%zu bytes):\n", decoded_len);
|
||||||
|
// handle_downlink_packet(decoded, decoded_len);
|
||||||
|
// } else {
|
||||||
|
// printf("Base64 decode failed\n");
|
||||||
|
// }
|
||||||
|
|
||||||
|
printTelemetryPacketNcurses(&telemetryPacket, &down);
|
||||||
|
while (1) {
|
||||||
|
char ch;
|
||||||
|
size_t n = read(fd, &ch, 1);
|
||||||
|
if (n > 0) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
buf[buf_pos] = '\0';
|
||||||
|
printf("%s\n", buf);
|
||||||
|
|
||||||
|
if (strncmp(buf, "Got DownLink data", 17) == 0) {
|
||||||
|
char *colon = strchr(buf, ':');
|
||||||
|
if (colon) {
|
||||||
|
colon++;
|
||||||
|
while (*colon == ' ') colon++; // skip spaces
|
||||||
|
printf("Detected base64 payload: %s\n", colon);
|
||||||
|
|
||||||
|
if (base64_decode(colon, decoded, &decoded_len) == 0) {
|
||||||
|
printf("Decoded data (%zu bytes):\n", decoded_len);
|
||||||
|
handle_downlink_packet(decoded, decoded_len);
|
||||||
|
} else {
|
||||||
|
printf("Base64 decode failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf_pos = 0;
|
||||||
|
} else {
|
||||||
|
if (buf_pos < sizeof(buf) - 1)
|
||||||
|
buf[buf_pos++] = ch;
|
||||||
|
}
|
||||||
|
} else if (n < 0) {
|
||||||
|
perror("read");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
init_crc32_table();
|
||||||
|
initscr(); // Start ncurses mode
|
||||||
|
start_color(); // Enable color
|
||||||
|
use_default_colors();
|
||||||
|
init_pair(1, COLOR_CYAN, -1);
|
||||||
|
init_pair(2, COLOR_GREEN, -1);
|
||||||
|
init_pair(3, COLOR_YELLOW, -1);
|
||||||
|
init_pair(4, COLOR_RED, -1);
|
||||||
|
init_pair(5, COLOR_CYAN, -1);
|
||||||
|
init_pair(6, COLOR_MAGENTA, -1);
|
||||||
|
curs_set(0); // Hide cursor
|
||||||
|
int x = subMain();
|
||||||
|
endwin(); // End ncurses mode
|
||||||
|
return x;
|
||||||
|
}
|
133
packets.h
Normal file
133
packets.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#ifndef PACKETS_STRUCTS
|
||||||
|
#define PACKETS_STRUCTS
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
#define UplinkSync "PlechHore"
|
||||||
|
#define DownlinkSync "PlechDole"
|
||||||
|
|
||||||
|
#define UplinkPacketType_SystemControl 0
|
||||||
|
#define UplinkPacketType_Ping 1
|
||||||
|
#define UplinkPacketType_ACK 255
|
||||||
|
|
||||||
|
#define DownlinkPacketType_Telemetry 0
|
||||||
|
#define DownlinkPacketType_Ping 1
|
||||||
|
#define DownlinkPacketType_ACK 255
|
||||||
|
|
||||||
|
#define BME680_PRESENT_BIT (1 << 0)
|
||||||
|
#define CCS811_PRESENT_BIT (1 << 1)
|
||||||
|
#define MPU9250_PRESENT_BIT (1 << 2)
|
||||||
|
#define INA260_PRESENT_BIT (1 << 3)
|
||||||
|
#define MCP23018_PRESENT_BIT (1 << 4)
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
char syncPhrase[10];
|
||||||
|
uint32_t packetIndex;
|
||||||
|
uint8_t packetType;
|
||||||
|
uint32_t missionTimer;
|
||||||
|
uint32_t CRCCheck;
|
||||||
|
} DownBoundPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
// MPU data
|
||||||
|
int16_t accelerationX;
|
||||||
|
int16_t accelerationY;
|
||||||
|
int16_t accelerationZ;
|
||||||
|
int16_t gyroX;
|
||||||
|
int16_t gyroY;
|
||||||
|
int16_t gyroZ;
|
||||||
|
int16_t magnetX;
|
||||||
|
int16_t magnetY;
|
||||||
|
int16_t magnetZ;
|
||||||
|
int16_t accelerometer_temperature;
|
||||||
|
|
||||||
|
// CCS data
|
||||||
|
uint16_t eCO2;
|
||||||
|
uint16_t tvoc;
|
||||||
|
uint8_t currentCCS;
|
||||||
|
uint16_t rawCCSData;
|
||||||
|
|
||||||
|
// INA data
|
||||||
|
uint16_t volts;
|
||||||
|
uint16_t current;
|
||||||
|
uint16_t power;
|
||||||
|
|
||||||
|
// BME DATA
|
||||||
|
uint32_t temperature;
|
||||||
|
uint16_t humidity;
|
||||||
|
uint32_t pressure;
|
||||||
|
uint16_t gas;
|
||||||
|
bool gas_valid;
|
||||||
|
bool heater_stable;
|
||||||
|
uint8_t gas_range;
|
||||||
|
uint8_t gas_index;
|
||||||
|
|
||||||
|
float air_temperature; /*!< air temperature in degrees celsius */
|
||||||
|
float relative_humidity; /*!< relative humidity in percent */
|
||||||
|
float barometric_pressure; /*!< barometric pressure in hecto-pascal */
|
||||||
|
float gas_resistance; /*!< gas resistance in ohms */
|
||||||
|
uint16_t iaq_score; /*!< air quality index (0..500) */
|
||||||
|
float temperature_score;
|
||||||
|
float humidity_score;
|
||||||
|
float gas_score;
|
||||||
|
|
||||||
|
// GPS DATA
|
||||||
|
uint32_t time_seconds; // Seconds since start of day
|
||||||
|
int32_t latitude_centi_degrees; // Latitude * 10,000
|
||||||
|
int32_t longitude_centi_degrees; // Longitude * 10,000
|
||||||
|
int16_t altitude_centi_meters; // Altitude * 100
|
||||||
|
uint8_t fix_quality;
|
||||||
|
uint8_t num_satellites;
|
||||||
|
uint16_t date_yyddmm; // YYDDMM (from GPRMC)
|
||||||
|
uint16_t speed_centi_knots; // Speed * 100 (from GPRMC)
|
||||||
|
|
||||||
|
int32_t predicted_latitude_centi_degrees; // Latitude * 10,000
|
||||||
|
int32_t predicted_longitude_centi_degrees; // Longitude * 10,000
|
||||||
|
int16_t predicted_altitude_centi_meters; // Altitude * 100
|
||||||
|
|
||||||
|
// ADC DATA
|
||||||
|
int32_t NH3;
|
||||||
|
int32_t CO;
|
||||||
|
int32_t NO2;
|
||||||
|
int32_t UVC;
|
||||||
|
|
||||||
|
int16_t currentServoA;
|
||||||
|
int16_t targetServoA;
|
||||||
|
int16_t currentServoB;
|
||||||
|
int16_t targetServoB;
|
||||||
|
|
||||||
|
uint8_t presentDevices;
|
||||||
|
|
||||||
|
uint8_t telemetryIndex;
|
||||||
|
|
||||||
|
} TelemetryPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
char syncPhrase[10];
|
||||||
|
uint32_t packetIndex;
|
||||||
|
uint8_t packetType;
|
||||||
|
uint32_t CRCCheck;
|
||||||
|
} UplinkPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
uint8_t powerMode;
|
||||||
|
uint8_t controlMode;
|
||||||
|
uint16_t servoA;
|
||||||
|
uint16_t servoB;
|
||||||
|
} SystemControlPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
uint8_t PingData[20];
|
||||||
|
} PingPacket;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
uint32_t packetIndex;
|
||||||
|
uint32_t crc32Checksum;
|
||||||
|
} ACKPacket;
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user