DMR-данные (SMS) на TYT MD-UV380 под OpenGD77
Восстановлено из транскрипта сессии после потери рабочего дерева на сервере сборки. Что внутри: - четыре новых модуля прошивки (приём, сборка исходящего, меню Messages); - правки существующих файлов в виде скриптов, с порядком применения; - инструменты стенда: снятие экрана рации по USB, дамп кадров, эталонная сборка пакета; - NOTES.md: что проверено в эфире, что не доделано, где грабли. Работало в эфире в августе 2026: данные ходят в обе стороны в режиме хотспота, рация принимает SMS и показывает на экране. Отправка собрана и сверена с эталоном сервера побайтово, но в эфире не проверялась. Claude-Session: https://claude.ai/code/session_01GJtvG5WwqYNe1b7s9LLtgy
Этот коммит содержится в:
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add Inbox/Outbox storage to the SMS module."""
|
||||
W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application"
|
||||
|
||||
# ------------------------------------------------------------------ header
|
||||
p = W + "/include/functions/sms.h"
|
||||
s = open(p).read()
|
||||
if "smsInboxCount" in s:
|
||||
print("sms.h: already has storage API")
|
||||
else:
|
||||
s = s.replace(
|
||||
"// Most recently received message, or NULL when nothing has been received yet.\n",
|
||||
"""// How many messages each folder keeps. Older ones are dropped when full.
|
||||
#define SMS_INBOX_SIZE 10
|
||||
#define SMS_OUTBOX_SIZE 10
|
||||
|
||||
// Inbox: messages received off air. Newest first.
|
||||
uint8_t smsInboxCount(void);
|
||||
const smsMessage_t *smsInboxGet(uint8_t index);
|
||||
void smsInboxClear(void);
|
||||
uint8_t smsInboxUnreadCount(void);
|
||||
|
||||
// Outbox: messages we sent. Newest first.
|
||||
uint8_t smsOutboxCount(void);
|
||||
const smsMessage_t *smsOutboxGet(uint8_t index);
|
||||
void smsOutboxClear(void);
|
||||
void smsOutboxAdd(uint32_t dstId, bool isPrivate, const char *text);
|
||||
|
||||
// Most recently received message, or NULL when nothing has been received yet.
|
||||
""", 1)
|
||||
open(p, "w").write(s)
|
||||
print("sms.h: storage API declared")
|
||||
|
||||
# ------------------------------------------------------------------ source
|
||||
p = W + "/source/functions/sms.c"
|
||||
s = open(p).read()
|
||||
if "smsInboxCount" in s:
|
||||
print("sms.c: already has storage")
|
||||
raise SystemExit(0)
|
||||
|
||||
a = "static smsMessage_t latest;\n"
|
||||
assert a in s
|
||||
s = s.replace(a, a + """
|
||||
// Message folders. Index 0 is always the newest; adding shifts the rest down.
|
||||
static smsMessage_t inbox[SMS_INBOX_SIZE];
|
||||
static uint8_t inboxCount = 0;
|
||||
static smsMessage_t outbox[SMS_OUTBOX_SIZE];
|
||||
static uint8_t outboxCount = 0;
|
||||
""", 1)
|
||||
|
||||
# clear folders on init
|
||||
s = s.replace("\tmemset(&latest, 0, sizeof(latest));\n",
|
||||
"\tmemset(&latest, 0, sizeof(latest));\n"
|
||||
"\tinboxCount = 0;\n"
|
||||
"\toutboxCount = 0;\n", 1)
|
||||
|
||||
# store a completed message into the inbox
|
||||
a = "\thaveMessage = (t > 0);\n"
|
||||
assert a in s, "completion point not found"
|
||||
s = s.replace(a, """ if (t > 0)
|
||||
{
|
||||
// Newest first: shift the existing entries down, dropping the oldest if full.
|
||||
for (int8_t i = (int8_t)((inboxCount < SMS_INBOX_SIZE) ? inboxCount : (SMS_INBOX_SIZE - 1)); i > 0; i--)
|
||||
{
|
||||
inbox[i] = inbox[i - 1];
|
||||
}
|
||||
|
||||
inbox[0] = latest;
|
||||
|
||||
if (inboxCount < SMS_INBOX_SIZE)
|
||||
{
|
||||
inboxCount++;
|
||||
}
|
||||
}
|
||||
|
||||
""" + a, 1)
|
||||
|
||||
s += """
|
||||
|
||||
uint8_t smsInboxCount(void)
|
||||
{
|
||||
return inboxCount;
|
||||
}
|
||||
|
||||
const smsMessage_t *smsInboxGet(uint8_t index)
|
||||
{
|
||||
return ((index < inboxCount) ? &inbox[index] : NULL);
|
||||
}
|
||||
|
||||
void smsInboxClear(void)
|
||||
{
|
||||
inboxCount = 0;
|
||||
unread = false;
|
||||
haveMessage = false;
|
||||
}
|
||||
|
||||
uint8_t smsInboxUnreadCount(void)
|
||||
{
|
||||
uint8_t n = 0;
|
||||
|
||||
for (uint8_t i = 0; i < inboxCount; i++)
|
||||
{
|
||||
if (inbox[i].unread)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
uint8_t smsOutboxCount(void)
|
||||
{
|
||||
return outboxCount;
|
||||
}
|
||||
|
||||
const smsMessage_t *smsOutboxGet(uint8_t index)
|
||||
{
|
||||
return ((index < outboxCount) ? &outbox[index] : NULL);
|
||||
}
|
||||
|
||||
void smsOutboxClear(void)
|
||||
{
|
||||
outboxCount = 0;
|
||||
}
|
||||
|
||||
void smsOutboxAdd(uint32_t dstId, bool isPrivate, const char *text)
|
||||
{
|
||||
for (int8_t i = (int8_t)((outboxCount < SMS_OUTBOX_SIZE) ? outboxCount : (SMS_OUTBOX_SIZE - 1)); i > 0; i--)
|
||||
{
|
||||
outbox[i] = outbox[i - 1];
|
||||
}
|
||||
|
||||
memset(&outbox[0], 0, sizeof(smsMessage_t));
|
||||
outbox[0].dstId = dstId;
|
||||
outbox[0].isPrivate = isPrivate;
|
||||
outbox[0].unread = false;
|
||||
strncpy(outbox[0].text, text, SMS_MAX_TEXT_LENGTH);
|
||||
outbox[0].text[SMS_MAX_TEXT_LENGTH] = '\\0';
|
||||
|
||||
if (outboxCount < SMS_OUTBOX_SIZE)
|
||||
{
|
||||
outboxCount++;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
open(p, "w").write(s)
|
||||
print("sms.c: inbox/outbox storage added")
|
||||
Ссылка в новой задаче
Block a user