antirez 3 лет назад
Родитель
Сommit
3f78a13e0a
5 измененных файлов с 183 добавлено и 5 удалено
  1. 3 1
      app.h
  2. 14 0
      crc.c
  3. 9 4
      fields.c
  4. 155 0
      protocols/pvchat.c
  5. 2 0
      signal.c

+ 3 - 1
app.h

@@ -307,7 +307,7 @@ void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint
 void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
 void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
 void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits);
-void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s);
+void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s, size_t len);
 void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count);
 void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot);
 const char *field_get_type_name(ProtoViewField *f);
@@ -319,3 +319,5 @@ void field_set_from_field(ProtoViewField *dst, ProtoViewField *src);
 
 /* crc.c */
 uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);
+uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t init);
+uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init);

+ 14 - 0
crc.c

@@ -21,3 +21,17 @@ uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly)
     }
     return crc;
 }
+
+/* Sum all the specified bytes modulo 256.
+ * Initialize the sum with 'init' (usually 0). */
+uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t init) {
+    for (size_t i = 0; i < len; i++) init += data[i];
+    return init;
+}
+
+/* Perform the bitwise xor of all the specified bytes.
+ * Initialize xor value with 'init' (usually 0). */
+uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init) {
+    for (size_t i = 0; i < len; i++) init ^= data[i];
+    return init;
+}

+ 9 - 4
fields.c

@@ -312,11 +312,16 @@ void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, ui
     fieldset_add_field(fs,f);
 }
 
-/* Allocate and append a string field. */
-void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s) {
+/* Allocate and append a string field. The string 's' does not need to point
+ * to a null terminated string, but must have at least 'len' valid bytes
+ * starting from the pointer. The field object will be correctly null
+ * terminated. */
+void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s, size_t len) {
     ProtoViewField *f = field_new(FieldTypeStr,name);
-    f->str = strdup(s);
-    f->len = strlen(s);
+    f->len = len;
+    f->str = malloc(len+1);
+    memcpy(f->str,s,len);
+    f->str[len] = 0;
     fieldset_add_field(fs,f);
 }
 

+ 155 - 0
protocols/pvchat.c

@@ -0,0 +1,155 @@
+#include "../app.h"
+
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * ----------------------------------------------------------
+ * ProtoView chat protocol. This is just a fun test protocol
+ * that can be used between two Flippers in order to send
+ * and receive text messages.
+ * ----------------------------------------------------------
+ *
+ * Protocol description
+ * ====================
+ *
+ * "1" represents a pulse of one-third bit time (100us)
+ * "0" represents a gap of one-third bit time (100us)
+ * 
+ * The message starts with a preamble + a sync pattern:
+ * 
+ * preamble = 10101010101010101010101010101010
+ * sync     = 1100110011001100
+ * 
+ * The a variable amount of bytes follow, where each bit
+ * is encoded in the following way:
+ * 
+ * zero 100 (100 us pulse, 200 us gap)
+ * one  110 (200 us pulse, 100 us gap)
+ * 
+ * Bytes are sent MSB first, so receiving, in sequence, bits
+ * 11100001, means byte E1.
+ * 
+ * This is the data format:
+ * 
+ * +-+------+-------+
+ * |L|Sender|Message|
+ * +-+------+-------+
+ *  |    |      \_Message, terminated by 0xFF + 0xAA + 1 byte of checksum
+ *  |    |      
+ *  |    \_ L bytes of sender name
+ *  \
+ *   \_ Length of sender in bytes
+ * 
+ * 
+ * Checksum = sum of bytes modulo 256, with checksum set
+ *            to 0 for the computation.
+ */
+
+static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
+    const char *sync_pattern = "1010101010101010" "1100110011001100";
+    uint8_t sync_len = 32;
+
+    // This is a variable length message, however the minimum length
+    // requires a sender len byte (of value zero) and the terminator
+    // FF 00 plus checksum: a total of 4 bytes.
+    if (numbits-sync_len < 8*4) return false;
+
+    uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
+    if (off == BITMAP_SEEK_NOT_FOUND) return false;
+    FURI_LOG_E(TAG, "Chat preamble+sync found");
+
+    info->start_off = off;
+    off += sync_len; /* Skip preamble and sync. */
+
+    uint8_t raw[64] = {(uint8_t)'.'};
+    uint32_t decoded =
+        convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
+            "100","110"); /* PWM */
+    FURI_LOG_E(TAG, "Chat decoded bits: %lu", decoded);
+
+    if (decoded < 8*4) return false; /* Min message len. */
+
+    // The message needs to have a two bytes terminator before
+    // the checksum.
+    uint32_t j;
+    for (j = 0; j < sizeof(raw)-2; j++)
+        if (raw[j] == 0xff && raw[j+1] == 0xaa) break;
+
+    if (j == sizeof(raw)-1) return false; // No terminator found.
+
+    uint32_t datalen = j+3; // If the terminator was found at j, then
+                            // we need to sum three more bytes to have
+                            // the len: FF itself, AA, checksum.
+    info->pulses_count = 8*3*datalen;
+
+    // Check if the control sum matches.
+    if (sum_bytes(raw,datalen-1,0) != raw[datalen-1]) return false;
+
+    // Check if the length of the sender looks sane
+    uint8_t senderlen = raw[0];
+    if (senderlen >= sizeof(raw)) return false; // Overflow
+
+    fieldset_add_str(info->fieldset,"sender",(char*)raw+1,senderlen);
+    fieldset_add_str(info->fieldset,"message",(char*)raw+1+senderlen,
+                     datalen-senderlen-3);
+    return true;
+}
+
+/* Give fields and defaults for the signal creator. */
+static void get_fields(ProtoViewFieldSet *fieldset) {
+    fieldset_add_str(fieldset,"sender","Carol",5);
+    fieldset_add_str(fieldset,"message","Anyone hearing?",15);
+}
+
+/* Create a signal. */
+static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs)
+{
+    uint32_t te = 100; /* Short pulse duration in microseconds.
+                          Our protocol needs three symbol times to send
+                          a bit, so 300 us per bit = 3.33 kBaud. */
+
+    // Preamble: 16 alternating 100us pulse/gap pairs.
+    for (int j = 0; j < 16; j++) {
+        raw_samples_add(samples,true,te);
+        raw_samples_add(samples,false,te);
+    }
+
+    // Sync: 4 alternating 200 us pulse/gap pairs.
+    for (int j = 0; j < 4; j++) {
+        raw_samples_add(samples,true,te*2);
+        raw_samples_add(samples,false,te*2);
+    }
+
+    // Data: build the array.
+    uint32_t datalen = 1 + fs->fields[0]->len + // Userlen + Username
+                       fs->fields[1]->len + 3;  // Message + FF + 00 + CRC
+    uint8_t *data = malloc(datalen), *p = data;
+    *p++ = fs->fields[0]->len;
+    memcpy(p,fs->fields[0]->str,fs->fields[0]->len);
+    p += fs->fields[0]->len;
+    memcpy(p,fs->fields[1]->str,fs->fields[1]->len);
+    p += fs->fields[1]->len;
+    *p++ = 0xff;
+    *p++ = 0xaa;
+    *p = sum_bytes(data,datalen-1,0);
+    FURI_LOG_E(TAG,"SUM: %lu", (unsigned long)*p);
+
+    // Emit bits
+    for (uint32_t j = 0; j < datalen*8; j++) {
+        if (bitmap_get(data,datalen,j)) {
+            raw_samples_add(samples,true,te*2);
+            raw_samples_add(samples,false,te);
+        } else {
+            raw_samples_add(samples,true,te);
+            raw_samples_add(samples,false,te*2);
+        }
+    }
+    free(data);
+}
+
+ProtoViewDecoder ProtoViewChatDecoder = {
+    .name = "ProtoView chat",
+    .decode = decode,
+    .get_fields = get_fields,
+    .build_message = build_message 
+};

+ 2 - 0
signal.c

@@ -526,6 +526,7 @@ extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder;
 extern ProtoViewDecoder CitroenTPMSDecoder;
 extern ProtoViewDecoder FordTPMSDecoder;
 extern ProtoViewDecoder KeeloqDecoder;
+extern ProtoViewDecoder ProtoViewChatDecoder;
 
 ProtoViewDecoder *Decoders[] = {
     &Oregon2Decoder,                /* Oregon sensors v2.1 protocol. */
@@ -537,6 +538,7 @@ ProtoViewDecoder *Decoders[] = {
     &CitroenTPMSDecoder,            /* Citroen TPMS. */
     &FordTPMSDecoder,               /* Ford TPMS. */
     &KeeloqDecoder,                 /* Keeloq remote. */
+    &ProtoViewChatDecoder,          /* Protoview simple text messages. */
     NULL
 };