mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-10-02 14:10:25 +02:00
Course update: lessons, CTF 0x0011a_cb, and documentation
- 0x0011a_cb (Operation Dark Vector): nation-state CTF redesign with an AES-128-ECB sealed target and a plaintext launch origin; RP2350 firmware with bearing-driven servo, tri-color LEDs, GSV stats, and a realistic no-fix path - docs: story-driven classified brief, GDB and Ghidra tutorials with deep step-throughs, regenerated artifacts and PDFs - scripts: docstring standard, AES per-student randomizer, telemetry monitor - week 3 to week 5 lessons: Ghidra patching tutorial, CMSIS-SVD hardware RE, double floating-point and GPIO architecture chapters, README structure
This commit is contained in:
1 parent
5201ee4b6b
commit
35eacd2c0e
162 files changed
+125658
-232
No files matched your search
@@ -0,0 +1,119 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// File: aes.c
|
||||
// Desc: Minimal AES-128-ECB block decryption for the CTF waypoint.
|
||||
// Created: 2026
|
||||
|
||||
#include "aes.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t sbox[256] = {
|
||||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
||||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
||||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
||||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
||||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
||||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
||||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
||||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
||||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
||||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
||||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
||||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
||||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
||||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
||||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
|
||||
};
|
||||
|
||||
static const uint8_t rsbox[256] = {
|
||||
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
||||
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
||||
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
||||
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
||||
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
||||
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
||||
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
||||
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
||||
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
||||
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
||||
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
||||
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
||||
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
||||
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
||||
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
|
||||
};
|
||||
|
||||
static const uint8_t Rcon[11] = {0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36};
|
||||
|
||||
static uint8_t xtime(uint8_t x) { return (uint8_t)((x << 1) ^ (((x >> 7) & 1) * 0x1B)); }
|
||||
|
||||
static uint8_t mul(uint8_t a, uint8_t b) {
|
||||
uint8_t p = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (b & 1) p ^= a;
|
||||
uint8_t hi = a & 0x80; a <<= 1;
|
||||
if (hi) a ^= 0x1B;
|
||||
b >>= 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void key_expansion(const uint8_t *key, uint8_t *rk) {
|
||||
memcpy(rk, key, 16);
|
||||
for (int i = 4; i < 44; i++) {
|
||||
uint8_t t[4];
|
||||
memcpy(t, &rk[(i - 1) * 4], 4);
|
||||
if (i % 4 == 0) {
|
||||
uint8_t tmp = t[0];
|
||||
t[0] = (uint8_t)(sbox[t[1]] ^ Rcon[i / 4]);
|
||||
t[1] = sbox[t[2]];
|
||||
t[2] = sbox[t[3]];
|
||||
t[3] = sbox[tmp];
|
||||
}
|
||||
for (int j = 0; j < 4; j++) rk[i * 4 + j] = rk[(i - 4) * 4 + j] ^ t[j];
|
||||
}
|
||||
}
|
||||
|
||||
static void add_round_key(uint8_t r, uint8_t *s, const uint8_t *rk) {
|
||||
for (int i = 0; i < 16; i++) s[i] ^= rk[r * 16 + i];
|
||||
}
|
||||
|
||||
static void inv_sub_bytes(uint8_t *s) { for (int i = 0; i < 16; i++) s[i] = rsbox[s[i]]; }
|
||||
|
||||
static void inv_shift_rows(uint8_t *s) {
|
||||
uint8_t t;
|
||||
t = s[13]; s[13] = s[9]; s[9] = s[5]; s[5] = s[1]; s[1] = t;
|
||||
t = s[2]; s[2] = s[10]; s[10] = t; t = s[6]; s[6] = s[14]; s[14] = t;
|
||||
t = s[3]; s[3] = s[7]; s[7] = s[11]; s[11] = s[15]; s[15] = t;
|
||||
}
|
||||
|
||||
static void inv_mix_columns(uint8_t *s) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint8_t a = s[i * 4], b = s[i * 4 + 1], c = s[i * 4 + 2], d = s[i * 4 + 3];
|
||||
s[i * 4] = (uint8_t)(mul(a, 14) ^ mul(b, 11) ^ mul(c, 13) ^ mul(d, 9));
|
||||
s[i * 4 + 1] = (uint8_t)(mul(a, 9) ^ mul(b, 14) ^ mul(c, 11) ^ mul(d, 13));
|
||||
s[i * 4 + 2] = (uint8_t)(mul(a, 13) ^ mul(b, 9) ^ mul(c, 14) ^ mul(d, 11));
|
||||
s[i * 4 + 3] = (uint8_t)(mul(a, 11) ^ mul(b, 13) ^ mul(c, 9) ^ mul(d, 14));
|
||||
}
|
||||
}
|
||||
|
||||
void aes128_ecb_decrypt_block(const uint8_t in[16], const uint8_t key[16], uint8_t out[16]) {
|
||||
uint8_t rk[176];
|
||||
key_expansion(key, rk);
|
||||
memcpy(out, in, 16);
|
||||
add_round_key(10, out, rk);
|
||||
for (int r = 9; r > 0; r--) {
|
||||
inv_shift_rows(out);
|
||||
inv_sub_bytes(out);
|
||||
add_round_key(r, out, rk);
|
||||
inv_mix_columns(out);
|
||||
}
|
||||
inv_shift_rows(out);
|
||||
inv_sub_bytes(out);
|
||||
add_round_key(0, out, rk);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: gps.c
|
||||
// Desc: Implements PIO UART GPS receiver and NMEA coordinate parsing.
|
||||
// Created: 2026
|
||||
|
||||
#include "gps.h"
|
||||
#include "uart_rx.pio.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int gps_siv = 0;
|
||||
static int gps_cno = 0;
|
||||
|
||||
void init_gps_pio(void)
|
||||
{
|
||||
uint offset = pio_add_program(GPS_PIO, &uart_rx_program);
|
||||
uart_rx_program_init(GPS_PIO, GPS_SM, offset, GPS_PIN, GPS_BAUD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert NMEA ddmm.mmmm coordinate to decimal degrees.
|
||||
*
|
||||
* @param str NMEA coordinate string.
|
||||
* @param dir Cardinal direction character ('N', 'S', 'E', 'W').
|
||||
* @return double Decimal degree coordinate.
|
||||
*/
|
||||
static double parse_nmea_coord(const char *str, char dir)
|
||||
{
|
||||
double raw = atof(str);
|
||||
int deg = (int)(raw / 100.0);
|
||||
double dec = (double)deg + ((raw - (deg * 100.0)) / 60.0);
|
||||
return ((dir == 'S') || (dir == 'W')) ? -dec : dec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find pointer to n-th comma-separated field in NMEA string.
|
||||
*
|
||||
* @param str NMEA sentence string.
|
||||
* @param field_idx Index of field to locate.
|
||||
* @return const char* Pointer to field start or NULL.
|
||||
*/
|
||||
static const char *get_nmea_field(const char *str, int field_idx)
|
||||
{
|
||||
while ((str != NULL) && (*str != '\0') && (field_idx > 0)) {
|
||||
if (*str++ == ',') {
|
||||
field_idx--;
|
||||
}
|
||||
}
|
||||
return (field_idx == 0) ? str : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse NMEA RMC sentence for valid coordinates.
|
||||
*
|
||||
* @param line NMEA sentence buffer.
|
||||
* @param lat Pointer to store parsed latitude.
|
||||
* @param lon Pointer to store parsed longitude.
|
||||
* @return None.
|
||||
*/
|
||||
static bool parse_rmc(const char *line, double *lat, double *lon)
|
||||
{
|
||||
const char *st = get_nmea_field(line, 2), *la = get_nmea_field(line, 3);
|
||||
const char *lo = get_nmea_field(line, 5);
|
||||
if (!st || *st != 'A' || !la || *la == ',' || !lo || *lo == ',') return false;
|
||||
*lat = parse_nmea_coord(la, *get_nmea_field(line, 4));
|
||||
*lon = parse_nmea_coord(lo, *get_nmea_field(line, 6));
|
||||
return (*lat != 0.0) && (*lon != 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse NMEA GSV sentence for satellites-in-view and best C/N0.
|
||||
*
|
||||
* @param line NMEA GSV sentence string.
|
||||
* @return None.
|
||||
*/
|
||||
static void parse_gsv(const char *line)
|
||||
{
|
||||
const char *sv = get_nmea_field(line, 3), *msg = get_nmea_field(line, 2);
|
||||
if (sv != NULL) gps_siv = atoi(sv);
|
||||
if ((msg != NULL) && (*msg == '1')) gps_cno = 0;
|
||||
for (int f = 7; f < 40; f += 4) {
|
||||
const char *c = get_nmea_field(line, f);
|
||||
if ((c == NULL) || (*c == '*') || (*c == '\0')) break;
|
||||
if (atoi(c) > gps_cno) gps_cno = atoi(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test buffered line for valid NMEA RMC sentence.
|
||||
*
|
||||
* @param buf NMEA character buffer.
|
||||
* @param idx Pointer to character index.
|
||||
* @param lat Pointer to current latitude.
|
||||
* @param lon Pointer to current longitude.
|
||||
* @return bool True if valid 3D fix was parsed, false otherwise.
|
||||
*/
|
||||
static bool check_rmc_line(char *buf, int *idx, double *lat, double *lon)
|
||||
{
|
||||
buf[*idx] = '\0';
|
||||
*idx = 0;
|
||||
char *rmc = strstr(buf, "RMC"), *gsv = strstr(buf, "GSV");
|
||||
if (gsv != NULL) parse_gsv(gsv);
|
||||
return (rmc != NULL) ? parse_rmc(rmc, lat, lon) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accumulate GPS character and trigger RMC parsing on newline.
|
||||
*
|
||||
* @param ch Received ASCII character.
|
||||
* @param lat Pointer to current latitude.
|
||||
* @param lon Pointer to current longitude.
|
||||
* @return bool True if a valid active 3D fix was parsed, false otherwise.
|
||||
*/
|
||||
static bool process_gps_char(char ch, double *lat, double *lon)
|
||||
{
|
||||
static char buf[96];
|
||||
static int idx = 0;
|
||||
if (ch == '$') idx = 0;
|
||||
if ((ch == '\n') || (ch == '\r'))
|
||||
return check_rmc_line(buf, &idx, lat, lon);
|
||||
if (idx < (int)(sizeof(buf) - 1))
|
||||
buf[idx++] = ch;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handle_gps_byte(double *lat, double *lon)
|
||||
{
|
||||
char ch = (char)(pio_sm_get(GPS_PIO, GPS_SM) >> 24);
|
||||
return process_gps_char(ch, lat, lon);
|
||||
}
|
||||
|
||||
bool poll_gps(double *lat, double *lon)
|
||||
{
|
||||
bool got_fix = false;
|
||||
while (!pio_sm_is_rx_fifo_empty(GPS_PIO, GPS_SM))
|
||||
got_fix |= handle_gps_byte(lat, lon);
|
||||
return got_fix;
|
||||
}
|
||||
|
||||
void gps_get_stats(int *siv, int *cno)
|
||||
{
|
||||
*siv = gps_siv;
|
||||
*cno = gps_cno;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: lcd.c
|
||||
// Desc: Implements I2C HD44780 16x2 LCD display for live telemetry coordinates.
|
||||
// Created: 2026
|
||||
|
||||
#include "lcd.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define PIN_RS 0x01
|
||||
#define PIN_EN 0x04
|
||||
#define BACKLIGHT 0x08
|
||||
|
||||
static uint8_t lcd_addr = 0x27;
|
||||
static bool lcd_ready = false;
|
||||
|
||||
static void pcf_write(uint8_t d)
|
||||
{
|
||||
if (lcd_ready)
|
||||
i2c_write_blocking(LCD_I2C_INST, lcd_addr, &d, 1, false);
|
||||
}
|
||||
|
||||
static void pcf_pulse(uint8_t d)
|
||||
{
|
||||
pcf_write(d | PIN_EN);
|
||||
sleep_us(1);
|
||||
pcf_write(d & ~PIN_EN);
|
||||
sleep_us(50);
|
||||
}
|
||||
|
||||
static void lcd_write4(uint8_t n, uint8_t mode)
|
||||
{
|
||||
uint8_t d = (n & 0x0F) << 4;
|
||||
d |= mode ? PIN_RS : 0;
|
||||
d |= BACKLIGHT;
|
||||
pcf_pulse(d);
|
||||
}
|
||||
|
||||
static void lcd_send(uint8_t v, uint8_t mode)
|
||||
{
|
||||
lcd_write4((v >> 4) & 0x0F, mode);
|
||||
lcd_write4(v & 0x0F, mode);
|
||||
}
|
||||
|
||||
static void lcd_clear(void)
|
||||
{
|
||||
lcd_send(0x01, 0);
|
||||
sleep_ms(2);
|
||||
}
|
||||
|
||||
static void lcd_set_cursor(int row, int col)
|
||||
{
|
||||
uint8_t offset = (row == 0) ? 0x00 : 0x40;
|
||||
lcd_send(0x80 | (col + offset), 0);
|
||||
}
|
||||
|
||||
static void lcd_puts(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
lcd_send((uint8_t)*s++, 1);
|
||||
}
|
||||
|
||||
static void lcd_reset_seq(void)
|
||||
{
|
||||
lcd_write4(0x03, 0); sleep_ms(5);
|
||||
lcd_write4(0x03, 0); sleep_us(150);
|
||||
lcd_write4(0x03, 0); sleep_us(150);
|
||||
lcd_write4(0x02, 0); sleep_us(150);
|
||||
}
|
||||
|
||||
static void lcd_cfg_seq(void)
|
||||
{
|
||||
lcd_send(0x28, 0);
|
||||
lcd_send(0x0C, 0);
|
||||
lcd_clear();
|
||||
lcd_send(0x06, 0);
|
||||
}
|
||||
|
||||
static bool detect_lcd(void)
|
||||
{
|
||||
uint8_t rx;
|
||||
if (i2c_read_blocking(LCD_I2C_INST, 0x27, &rx, 1, false) >= 0)
|
||||
return (lcd_addr = 0x27, true);
|
||||
if (i2c_read_blocking(LCD_I2C_INST, 0x3F, &rx, 1, false) >= 0)
|
||||
return (lcd_addr = 0x3F, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
void init_lcd(void)
|
||||
{
|
||||
i2c_init(LCD_I2C_INST, LCD_BAUD);
|
||||
gpio_set_function(LCD_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(LCD_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(LCD_SDA_PIN); gpio_pull_up(LCD_SCL_PIN);
|
||||
if (!(lcd_ready = detect_lcd())) return;
|
||||
lcd_reset_seq();
|
||||
lcd_cfg_seq();
|
||||
}
|
||||
|
||||
void lcd_show_coords(double lat, double lon)
|
||||
{
|
||||
if (!lcd_ready && !(lcd_ready = detect_lcd())) return;
|
||||
char r1[17], r2[17];
|
||||
snprintf(r1, sizeof(r1), "LAT: %9.6f %c", fabs(lat), (lat >= 0.0) ? 'N' : 'S');
|
||||
snprintf(r2, sizeof(r2), "LON: %9.6f %c", fabs(lon), (lon >= 0.0) ? 'E' : 'W');
|
||||
lcd_set_cursor(0, 0); lcd_puts(r1);
|
||||
lcd_set_cursor(1, 0); lcd_puts(r2);
|
||||
}
|
||||
|
||||
void lcd_show_gnss(int sats, int cno)
|
||||
{
|
||||
if (!lcd_ready && !(lcd_ready = detect_lcd())) return;
|
||||
char r1[17], r2[17];
|
||||
snprintf(r1, sizeof(r1), "SAT:%2d CNO:%2d ", sats, cno);
|
||||
snprintf(r2, sizeof(r2), "%-16s", (sats > 0) ? "ACQUIRING..." : "NO SIGNAL");
|
||||
lcd_set_cursor(0, 0); lcd_puts(r1);
|
||||
lcd_set_cursor(1, 0); lcd_puts(r2);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: lora.c
|
||||
// Desc: Implements UART1 driver for REYAX RYLR998 LoRa transceiver.
|
||||
// Created: 2026
|
||||
|
||||
#include "lora.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void drain_lora_rx(void)
|
||||
{
|
||||
while (uart_is_readable(LORA_UART)) {
|
||||
char c = (char)uart_getc(LORA_UART);
|
||||
if (c >= 32 && c <= 126)
|
||||
putchar(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_at_cmd(const char *cmd)
|
||||
{
|
||||
uart_write_blocking(LORA_UART, (const uint8_t *)cmd, strlen(cmd));
|
||||
sleep_ms(250);
|
||||
drain_lora_rx();
|
||||
}
|
||||
|
||||
static void configure_lora_rf(void)
|
||||
{
|
||||
sleep_ms(1500);
|
||||
send_at_cmd("AT\r\n");
|
||||
send_at_cmd("AT+NETWORKID=18\r\n");
|
||||
send_at_cmd("AT+BAND=915000000\r\n");
|
||||
send_at_cmd("AT+PARAMETER=9,7,1,12\r\n");
|
||||
send_at_cmd("AT+ADDRESS=2\r\n");
|
||||
}
|
||||
|
||||
void init_lora(void)
|
||||
{
|
||||
uart_init(LORA_UART, LORA_BAUD);
|
||||
uart_set_translate_crlf(LORA_UART, false);
|
||||
gpio_set_function(LORA_TX_PIN, GPIO_FUNC_UART);
|
||||
gpio_set_function(LORA_RX_PIN, GPIO_FUNC_UART);
|
||||
configure_lora_rf();
|
||||
}
|
||||
|
||||
static char tx_buf[160];
|
||||
static int tx_len = 0;
|
||||
static int tx_idx = 0;
|
||||
|
||||
void lora_send(const char *msg)
|
||||
{
|
||||
int len = (int)strlen(msg);
|
||||
while ((len > 0) && ((msg[len - 1] == '\r') || (msg[len - 1] == '\n')))
|
||||
len--;
|
||||
tx_len = snprintf(tx_buf, sizeof(tx_buf), "AT+SEND=0,%d,%.*s\r\n", len, len, msg);
|
||||
tx_idx = 0;
|
||||
}
|
||||
|
||||
void lora_tick(void)
|
||||
{
|
||||
while ((tx_idx < tx_len) && uart_is_writable(LORA_UART))
|
||||
uart_putc_raw(LORA_UART, (uint8_t)tx_buf[tx_idx++]);
|
||||
drain_lora_rx();
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: main.c
|
||||
// Desc: Main entry point for autonomous micro-UAV guidance firmware.
|
||||
// Created: 2026
|
||||
|
||||
#include "gps.h"
|
||||
#include "lora.h"
|
||||
#include "payload.h"
|
||||
#include "propeller.h"
|
||||
#include "navigation.h"
|
||||
#include "lcd.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/pio.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize all board peripherals, communications, and actuators.
|
||||
*
|
||||
* @param None.
|
||||
* @return None.
|
||||
*/
|
||||
static void init_all(void)
|
||||
{
|
||||
stdio_init_all();
|
||||
init_navigation();
|
||||
init_payload();
|
||||
init_lora();
|
||||
init_gps_pio();
|
||||
init_propeller();
|
||||
init_lcd();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Continually drain GPS PIO FIFO over 1-second flight tick.
|
||||
*
|
||||
* @param cur_lat Pointer to current latitude.
|
||||
* @param cur_lon Pointer to current longitude.
|
||||
* @return bool True if active 3D lock was parsed, false otherwise.
|
||||
*/
|
||||
static bool update_position(double *cur_lat, double *cur_lon)
|
||||
{
|
||||
bool got_fix = false;
|
||||
for (int i = 0; i < 200; i++, sleep_ms(5)) {
|
||||
got_fix |= poll_gps(cur_lat, cur_lon);
|
||||
lora_tick();
|
||||
}
|
||||
return got_fix;
|
||||
}
|
||||
|
||||
static void step_mission(double *cur_lat, double *cur_lon)
|
||||
{
|
||||
int siv = 0, cno = 0;
|
||||
static int hold = 0;
|
||||
bool fix = update_position(cur_lat, cur_lon);
|
||||
gps_get_stats(&siv, &cno);
|
||||
hold = fix ? 3 : ((hold > 0) ? (hold - 1) : 0);
|
||||
bool have = fix || (hold > 0);
|
||||
set_gnss_leds(have, siv);
|
||||
if (have) {
|
||||
lcd_show_coords(*cur_lat, *cur_lon);
|
||||
navigate_to_target(*cur_lat, *cur_lon);
|
||||
} else {
|
||||
lcd_show_gnss(siv, cno);
|
||||
propeller_stop();
|
||||
send_telemetry(*cur_lat, *cur_lon);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Autonomous micro-UAV firmware execution loop.
|
||||
*
|
||||
* @param None.
|
||||
* @return int Standard exit code (never reached in embedded firmware).
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
double cur_lat = ORIGIN_LAT, cur_lon = ORIGIN_LON;
|
||||
init_all();
|
||||
while (true)
|
||||
step_mission(&cur_lat, &cur_lon);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: navigation.c
|
||||
// Desc: Implements waypoint navigation, dead-reckoning, and telemetry dispatch.
|
||||
// Created: 2026
|
||||
|
||||
#include "navigation.h"
|
||||
#include "ctf_target.h"
|
||||
#include "aes.h"
|
||||
#include "lora.h"
|
||||
#include "payload.h"
|
||||
#include "propeller.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
double TARGET_LAT = 0.0;
|
||||
double TARGET_LON = 0.0;
|
||||
|
||||
const double ORIGIN_LAT = 38.840280;
|
||||
const double ORIGIN_LON = -77.428890;
|
||||
|
||||
void init_navigation(void)
|
||||
{
|
||||
uint8_t pt[16];
|
||||
const uint8_t key[16] = CTF_AES_KEY;
|
||||
const uint8_t ct[16] = CTF_TARGET_CT;
|
||||
aes128_ecb_decrypt_block(ct, key, pt);
|
||||
memcpy(&TARGET_LAT, pt, sizeof(double));
|
||||
memcpy(&TARGET_LON, pt + 8, sizeof(double));
|
||||
}
|
||||
|
||||
void send_telemetry(double cur_lat, double cur_lon)
|
||||
{
|
||||
char msg[80];
|
||||
snprintf(msg, sizeof(msg), "CURRENT LAT: %lf, LON: %lf\r\n", cur_lat, cur_lon);
|
||||
printf("%s", msg);
|
||||
lora_send(msg);
|
||||
}
|
||||
|
||||
void dead_reckon_step(double *cur_lat, double *cur_lon)
|
||||
{
|
||||
double dlat = TARGET_LAT - *cur_lat;
|
||||
double dlon = TARGET_LON - *cur_lon;
|
||||
*cur_lat += (fabs(dlat) < 0.005) ? dlat : ((dlat > 0.0) ? 0.004166 : -0.004166);
|
||||
*cur_lon += (fabs(dlon) < 0.005) ? dlon : ((dlon > 0.0) ? 0.002139 : -0.002139);
|
||||
}
|
||||
|
||||
bool check_arrival(double cur_lat, double cur_lon)
|
||||
{
|
||||
return (cur_lat == TARGET_LAT) && (cur_lon == TARGET_LON);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute the initial bearing from one point toward another.
|
||||
*
|
||||
* @param lat1 Origin latitude in degrees.
|
||||
* @param lon1 Origin longitude in degrees.
|
||||
* @param lat2 Destination latitude in degrees.
|
||||
* @param lon2 Destination longitude in degrees.
|
||||
* @return double Bearing in degrees (0 to 360).
|
||||
*/
|
||||
static double bearing_to(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
double p1 = lat1 * 0.017453292519943295, p2 = lat2 * 0.017453292519943295;
|
||||
double dl = (lon2 - lon1) * 0.017453292519943295;
|
||||
double y = sin(dl) * cos(p2);
|
||||
double x = cos(p1) * sin(p2) - sin(p1) * cos(p2) * cos(dl);
|
||||
double b = atan2(y, x) * 57.29577951308232;
|
||||
return (b < 0.0) ? (b + 360.0) : b;
|
||||
}
|
||||
|
||||
void navigate_to_target(double cur_lat, double cur_lon)
|
||||
{
|
||||
send_telemetry(cur_lat, cur_lon);
|
||||
if (check_arrival(cur_lat, cur_lon)) {
|
||||
propeller_stop();
|
||||
release_payload();
|
||||
} else {
|
||||
propeller_set_bearing(bearing_to(cur_lat, cur_lon, TARGET_LAT, TARGET_LON));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: payload.c
|
||||
// Desc: Implements payload release solenoid latch control on GPIO16.
|
||||
// Created: 2026
|
||||
|
||||
#include "payload.h"
|
||||
#include "lora.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void init_payload(void)
|
||||
{
|
||||
gpio_init(16); gpio_set_dir(16, GPIO_OUT); gpio_put(16, 1);
|
||||
gpio_init(17); gpio_set_dir(17, GPIO_OUT); gpio_put(17, 0);
|
||||
gpio_init(18); gpio_set_dir(18, GPIO_OUT); gpio_put(18, 0);
|
||||
gpio_init(25); gpio_set_dir(25, GPIO_OUT); gpio_put(25, 0);
|
||||
}
|
||||
|
||||
void set_gnss_leds(bool fix, int siv)
|
||||
{
|
||||
gpio_put(16, (!fix && (siv == 0)) ? 1 : 0);
|
||||
gpio_put(17, fix ? 1 : 0);
|
||||
gpio_put(18, (!fix && (siv > 0)) ? 1 : 0);
|
||||
}
|
||||
|
||||
void release_payload(void)
|
||||
{
|
||||
gpio_put(16, 1);
|
||||
gpio_put(17, 1);
|
||||
gpio_put(18, 1);
|
||||
printf("PAYLOAD RELEASED AT TARGET COORDINATES\r\n");
|
||||
lora_send("PAYLOAD RELEASED AT TARGET COORDINATES\r\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2026 Kevin Thomas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Author: Kevin Thomas
|
||||
// Email: kevin@mytechnotalent.com
|
||||
// GitHub: https://github.com/mytechnotalent
|
||||
// File: propeller.c
|
||||
// Desc: Implements SG90 servo PWM mock propeller control on GPIO6.
|
||||
// Created: 2026
|
||||
|
||||
#include "propeller.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
static struct repeating_timer prop_timer;
|
||||
static bool prop_active = false;
|
||||
static uint16_t current_pulse = 1000;
|
||||
|
||||
void init_propeller(void)
|
||||
{
|
||||
gpio_set_function(PROPELLER_PIN, GPIO_FUNC_PWM);
|
||||
uint s = pwm_gpio_to_slice_num(PROPELLER_PIN);
|
||||
pwm_set_clkdiv(s, 150.0f);
|
||||
pwm_set_wrap(s, 19999);
|
||||
pwm_set_gpio_level(PROPELLER_PIN, 0);
|
||||
pwm_set_enabled(s, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Repeating timer callback to alternate servo angle at max slew rate.
|
||||
*
|
||||
* @param t Pointer to repeating timer structure.
|
||||
* @return bool Always true to continue recurring timer.
|
||||
*/
|
||||
static bool prop_timer_callback(struct repeating_timer *t)
|
||||
{
|
||||
(void)t;
|
||||
current_pulse = (current_pulse == 1000) ? 2000 : 1000;
|
||||
pwm_set_gpio_level(PROPELLER_PIN, current_pulse);
|
||||
return true;
|
||||
}
|
||||
|
||||
void propeller_spin(void)
|
||||
{
|
||||
if (!prop_active) {
|
||||
prop_active = true;
|
||||
add_repeating_timer_ms(-150, prop_timer_callback, NULL, &prop_timer);
|
||||
}
|
||||
}
|
||||
|
||||
void propeller_set_bearing(double deg)
|
||||
{
|
||||
uint16_t pulse = (uint16_t)(1000.0 + (deg / 360.0) * 1000.0);
|
||||
pwm_set_gpio_level(PROPELLER_PIN, pulse);
|
||||
}
|
||||
|
||||
void propeller_stop(void)
|
||||
{
|
||||
if (prop_active) {
|
||||
cancel_repeating_timer(&prop_timer);
|
||||
prop_active = false;
|
||||
}
|
||||
pwm_set_gpio_level(PROPELLER_PIN, 0);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
;
|
||||
; Copyright (c) 2026 Kevin Thomas
|
||||
; SPDX-License-Identifier: MIT
|
||||
;
|
||||
.pio_version 0
|
||||
|
||||
.program uart_rx
|
||||
|
||||
; 8n1 UART receiver for GPS NMEA reception on a single GPIO pin.
|
||||
; Operates at 8 execution cycles per bit period.
|
||||
; IN pin 0 and JMP pin are mapped to the GPS RX GPIO pin.
|
||||
|
||||
start:
|
||||
wait 0 pin 0 ; Wait for start bit falling edge (1 cycle)
|
||||
set x, 7 [10] ; Preload bit counter (7 remaining), delay 1.5 bit periods (11 cycles)
|
||||
bitloop:
|
||||
in pins, 1 ; Sample 1 bit from RX pin into ISR (1 cycle)
|
||||
jmp x-- bitloop [6] ; Loop 8 times; each iteration is 8 execution cycles (7 cycles delay)
|
||||
jmp pin good_stop ; Verify stop bit is HIGH
|
||||
wait 1 pin 0 ; Framing error: wait until line returns to idle HIGH
|
||||
jmp start ; Discard frame and re-synchronize
|
||||
good_stop:
|
||||
push noblock ; Push 8-bit byte into RX FIFO (bits [31:24])
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
gpio_pull_up(pin);
|
||||
pio_sm_config c = uart_rx_program_get_default_config(offset);
|
||||
sm_config_set_in_pins(&c, pin);
|
||||
sm_config_set_jmp_pin(&c, pin);
|
||||
sm_config_set_in_shift(&c, true, false, 32);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
Reference in new issue
Block a user