Ejemplo n.º 1
0
OT_WEAK void dll_systask_beacon(ot_task task) {
/// The beacon rountine runs as an idependent systask.
    m2session*  b_session;

    if ((task->event == 0) || (dll.netconf.b_attempts == 0)) {
        dll_idle();
        return;
    }

    /// Load file-based beacon:
    /// Open BTS ISF Element and read the beacon sequence.  Make sure there is
    /// a beacon file of non-zero length and that beacons are presently enabled.
    if (dll.netconf.dd_flags == 0) {
        ot_u16  scratch;
        vlFILE* fp;

        fp = ISF_open_su( ISF_ID(beacon_transmit_sequence) );
        if (fp == NULL) {
            return; //goto dll_systask_beacon_STOP;
        }
        if (fp->length == 0)    {
            vl_close(fp);
            return; //goto dll_systask_beacon_STOP;
        }

        // a little hack
        scratch     = fp->start;
        fp->start  += task->cursor;

        /// Beacon List Management:
        /// <LI> Move cursor onto next beacon period (above, +8)</LI>
        /// <LI> Loop cursor if it is past the length of the list </LI>
        /// <LI> In special case where cursor = 254, everything still works! </LI>
        task->cursor   += 8;
        task->cursor    = (task->cursor >= fp->length) ? 0 : task->cursor;

        // Load next beacon into btemp, then undo the start hack, then close
        vl_load(fp, 8, dll.netconf.btemp);
        fp->start = scratch;
        vl_close(fp);
    }

    // First 2 bytes: Chan ID, Cmd Code
    // - Setup beacon ad-hoc session, on specified channel (ad hoc sessions never return NULL)
    // - Assure cmd code is always Broadcast & Announcement
    b_session           = session_new(  &dll_beacon_applet, 0, dll.netconf.btemp[0],
                                        (M2_NETSTATE_INIT | M2_NETSTATE_REQTX | M2_NETFLAG_FIRSTRX)  );
    b_session->subnet   = dll.netconf.b_subnet;
    b_session->extra    = dll.netconf.btemp[1];
    b_session->flags    = dll.netconf.btemp[1] & 0x78;
    //b_session->flags   |= (b_session->extra & 0x30);

    // Last 2 bytes: Next Scan ticks
    sys_task_setnext(task, TI2CLK( PLATFORM_ENDIAN16(*(ot_u16*)&dll.netconf.btemp[6]) ));

    ///@note this might not be necessary or wise!
    //return;
    //dll_systask_beacon_STOP:
    //dll_idle();
}
Ejemplo n.º 2
0
OT_WEAK void rm2_init(void) {
    vlFILE* fp;
    
    /// Set universal Radio module initialization defaults
    radio.state     = RADIO_Idle;
    radio.evtdone   = &otutils_sig2_null;

    /// These Radio Link features are available on the SPIRIT1
    ///@todo see if this and the "Set startup channel" part of the init can
    ///      get bundled into a common initialization function in radio_task.c
#   if (OT_FEATURE(RF_LINKINFO))
#       if (M2_FEATURE(RSCODE))
#           define _CORRECTIONS RADIO_LINK_CORRECTIONS
#       else
#           define _CORRECTIONS 0
#       endif
    radio.link.flags        = _CORRECTIONS | RADIO_LINK_PQI | RADIO_LINK_SQI \
                            | RADIO_LINK_LQI | RADIO_LINK_AGC;
#   endif
    radio.link.offset_thr   = 0;
    radio.link.raw_thr      = 0;
    
    /// Set startup channel to an always invalid channel ID (0xF0), and run 
    /// lookup on the default channel (0x18) to kick things off.  Since the 
    /// startup channel will always be different than a real channel, the 
    /// necessary settings and calibration will always occur. 
    phymac[0].channel   = 0xF0;
    phymac[0].tx_eirp   = 0x7F;
    fp                  = ISF_open_su( ISF_ID(channel_configuration) );
    rm2_channel_lookup(0x18, fp);
    vl_close(fp);
}
Ejemplo n.º 3
0
ot_int sub_filecreate(alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in) {
    ot_int  data_out    = 0;
    vlBLOCK file_block  = (vlBLOCK)((cmd_in >> 4) & 0x07);
    
    while ((data_in > 0) && sub_qnotfull(respond, 2, alp->outq)) {
        vlFILE*     fp = NULL;
        ot_u8       id;
        ot_u8       mod;
        ot_u16      alloc;
        ot_u8       err_code;
        
        data_in            -= 6;
        id                  = *alp->inq->getcursor++;
        mod                 = *alp->inq->getcursor;
        alp->inq->getcursor+= 3;                        // cursor goes past mod+length (length ignored)
        alloc               = q_readshort(alp->inq);
        err_code            = vl_new(&fp, file_block, id, mod, alloc, user_id);
        
        if (respond) {
            q_writebyte(alp->outq, id);
            q_writebyte(alp->outq, err_code);
            data_out += 2;
        }
        
        vl_close(fp);
    }
    
    //alp->BOOKMARK_IN = (void*)sub_testchunk(data_in);
    return data_out;      
}
Ejemplo n.º 4
0
void AES_load_static_key(ot_u8 key_id, ot_u32* key) {
    ot_int i;
    vlFILE* fp;
    
    fp = ISF_open_su(key_id);
    for (i=0; i<16; i+=2) {
        *(ot_u16*)((ot_u8*)key+i) = vl_read(fp, i);
    }
    vl_close(fp);
}
Ejemplo n.º 5
0
OT_WEAK void dll_refresh(void) {
    ot_uni16 scratch;
    vlFILE* fp;

    /// Open Network Features ISF and load the values from that file into the
    /// cached dll.netconf settings.

    fp = ISF_open_su(0);
    vl_load(fp, 10, dll.netconf.vid);
    dll.netconf.dd_flags    = 0;
    dll.netconf.hold_limit  = PLATFORM_ENDIAN16(dll.netconf.hold_limit);
    vl_close(fp);

    fp = ISF_open_su(1);
    vl_load(fp, 8, dll.netconf.uid);
    vl_close(fp);

    // Reset the Scheduler (only does anything if scheduler is implemented)
    dll_refresh_rts();
    sub_dll_flush();
}
Ejemplo n.º 6
0
void sub_build_uhfmsg(ot_int* buffer) {
/// This is the routine that builds the DASH7 UDP generic protocol message.
/// The protocol has data elements marked by a letter (T, V, R, E, D) that
/// signify Temperature, Voltage, RSSI (LF), PaLFi wake Event, and RX Data.
/// The elements are fixed/known length.
    command_tmpl    c_tmpl;
    ot_u8*          data_start;
    ot_u8           status;

    // Broadcast request (takes no 2nd argument)
    otapi_open_request(ADDR_broadcast, NULL);

    // Insert Transport-Layer headers
    c_tmpl.type     = CMDTYPE_na2p_request;
    c_tmpl.opcode   = CMD_udp_on_file;
    c_tmpl.extension= CMDEXT_no_response;
    otapi_put_command_tmpl(&status, &c_tmpl);
    otapi_put_dialog_tmpl(&status, NULL);       // NULL = defaults

    // UDP Header
    q_writebyte(&txq, 255);        // Source Port: 255 (custom application port)
    q_writebyte(&txq, 255);        // Destination Port (same value)

    data_start = txq.putcursor;

    // Place temperature data
    q_writebyte(&txq, 'T');
    q_writeshort(&txq, buffer[0]);

    // Place Voltage data
    q_writebyte(&txq, 'V');
    q_writeshort(&txq, buffer[1]);

    // Place RSSI data
    q_writebyte(&txq, 'R');
    q_writeshort(&txq, radio.last_rssi);


    // Store this information into the Port 255 file for continuous, automated
    // reporting by DASH7/OpenTag until it is updated next time.  The length of
    // this information is always 6 bytes.
    {
        vlFILE* fp;
        fp = ISF_open_su(255);
        if (fp != NULL) {
            vl_store(fp, 6, data_start);
            vl_close(fp);
        }
    }

    // Finish Message
    otapi_close_request();
}
Ejemplo n.º 7
0
/** ALP Processor Callback for Starting a Ping <BR>
  * ========================================================================<BR>
  * "ALP" is the NDEF-based set of low-level API protocols that OpenTag uses.
  * ALP messages can come-in over any communication method: wire, wireless, 
  * telepathy... anything that can transfer a packet payload.
  *
  * Some ALPs are standardized. Those get handled by OTlib automatically.  ALPs
  * that are not recognized are sent to this function to be handled.  In this
  * demo, we are using a very simple ALP, shown below:
  *
  * ALP Payload Length:         0
  * ALP Protocol ID:            255 (FF)
  * ALP Protocol Commands:      0-127 (00-7F) corresponding to channel to sniff
  *
  * The "user_id" parameter corresponds to the Device ID that sent this ALP.
  * 
  * A quickstart guide to the ALP API is available on the Indigresso Wiki.
  * http://www.indigresso.com/wiki/doku.php?id=opentag:api:quickstart
  */ 
void otapi_alpext_proc(alp_tmpl* alp, id_tmpl* user_id) {
/// The function app_invoke() will cause the kernel to call ext_systask() as
/// soon as resources are available.
    vlFILE*     fp;
    ot_uni16    scratch;
    ot_u8       channel;
    ot_u8       retval;

    // Start the task only if: Caller is ROOT, ALP Call is Protocol-255, Task is idle
    if (    auth_isroot(user_id)    \
        &&  (alp->inrec.id == 0xFF) \
        &&  (APP_TASK.event == 0)   )   {
        
        /// Make sure channel is spec-legal.  If so, set the channel of the 
        /// first (and only) in the channel list to the specified one, and also
        /// set the channel of the hold scan accordingly.  By default, the scan
        /// is updated every second.  The next scan will have these settings.
        retval  = 0;
        channel = alp->inrec.cmd & 0x7F;
        if (((channel & 0xF0) <= 0x20) && ((channel & 0x0F) <= 0x0E)) {
            fp                  = ISF_open_su(ISF_ID(channel_configuration));
            scratch.ushort      = vl_read(fp, 0);
            scratch.ubyte[0]    = channel;
            vl_write(fp, 0, scratch.ushort);
            vl_close(fp);
            
            fp                  = ISF_open_su(ISF_ID(hold_scan_sequence));
            scratch.ushort      = vl_read(fp, 0);
            scratch.ubyte[0]    = channel;
            vl_write(fp, 0, scratch.ushort);
            vl_close(fp);
            
            retval = 1; //success
        }
        
        alp_load_retval(alp, retval);
    }
}
Ejemplo n.º 8
0
OT_WEAK void dll_change_settings(ot_u16 new_mask, ot_u16 new_settings) {
    vlFILE* fp_active;
    vlFILE* fp_supported;

    // Get Active Settings, Get Supported Settings,
    // Mask-out unsupported settings, apply to new active settings
    ///@todo assert fp
    fp_active           = ISF_open_su( 0x00 );
    fp_supported        = ISF_open_su( 0x01 );
    new_mask           &= vl_read(fp_supported, 8);
    dll.netconf.active  = vl_read(fp_active, 4);
    new_settings       &= new_mask;
    dll.netconf.active &= ~new_mask;
    dll.netconf.active |= new_settings;

    // Write the new settings to the ISF 0
    vl_write(fp_active, 4, dll.netconf.active);
    vl_close(fp_active);
    vl_close(fp_supported);

    // Flush the System of all Sessions and Events, and restart it
    sub_dll_flush();
}
Ejemplo n.º 9
0
void m2np_put_deviceid(ot_bool use_vid) {
    vlFILE* fp;
    
    //file 0=network_settings, 1=device_features
    fp = ISF_open_su( (ot_u8)(use_vid == False) );
    ///@todo assert fp

    q_writeshort_be( &txq, vl_read(fp, 0) );
    if (use_vid == False) {
    	q_writeshort_be( &txq, vl_read(fp, 2) );
    	q_writeshort_be( &txq, vl_read(fp, 4) );
    	q_writeshort_be( &txq, vl_read(fp, 6) );
    }
    
    vl_close(fp);
}
Ejemplo n.º 10
0
int main (void)
{
	VL_CONNECTION	*vl_conn[NUM_CONNS];
	int				ret_val;
	char			buff[VL_MAX_ERRMSG_LEN];
	unsigned int	err_code;
	int				i;

	
	for (i = 0; i <= NUM_CONNS; i++)
	{

		ret_val = vl_open(NULL, &vl_conn[i]);

		if (ret_val == VL_ERROR)
		{
			vl_get_error (vl_conn[i], &err_code);
			printf ("\nError: Connection %d rejected! %s\n", (i+1), vl_err_string(err_code));
		}
		else
		{			
			printf ("\nConnection %d accepted...", (i+1));
		
			sprintf (buff, "echo 'hi from connection %d!'", (i+1));

			ret_val = vl_exec(vl_conn[i], buff);
			
			if (ret_val != VL_OK)
			{
				printf ("\n");
				printf ("\nError: ret_val of vl_exec is %d",ret_val);
			}
		}

	}


	
	
	for (i = 0; i <= NUM_CONNS; i++)
	{
		vl_close(vl_conn[i]);
	}

	printf ("\n");
	return (0);
}
Ejemplo n.º 11
0
ot_bool m2np_idcmp(ot_int length, void* id) {
    ot_bool check   = True;
    ot_u8   use_uid = (length == 8);
    vlFILE* fp;
    
    //file 0=network_settings, 1=device_features
    fp      = ISF_open_su( use_uid );
    check  &= ( ((ot_u16*)id)[0] == vl_read(fp, 0));

    if (use_uid) {
        check &= ( ((ot_u16*)id)[1] == vl_read(fp, 2) );
        check &= ( ((ot_u16*)id)[2] == vl_read(fp, 4) );
        check &= ( ((ot_u16*)id)[3] == vl_read(fp, 6) );
    }
    
    vl_close(fp);
    return check;
}
Ejemplo n.º 12
0
OT_WEAK void dll_systask_sleepscan(ot_task task) {
/// The Sleep Scan process runs as an independent task.  It is very similar
/// to the Hold Scan process, which actually calls this same routine.  They
/// use independent task markers, however, so they behave differently.
    ot_u8       s_channel;
    ot_u8       s_code;
    ot_uni16    scratch;
    vlFILE*     fp;
    m2session*  s_new;

    /// event = 0 is the initializer, but there is no state-based operation
    /// or any heap-stored data for this task, so it does nothing
    if (task->event == 0) {
        return;
    }

    fp = ISF_open_su( task->event );
    ///@note fp doesn't really need to be asserted unless you are mucking
    ///      with things in test builds.

    /// Pull channel ID and Scan flags
    scratch.ushort  = vl_read(fp, task->cursor);
    s_channel       = scratch.ubyte[0];
    s_code          = scratch.ubyte[1];

    /// Set the next idle event from the two-byte Next Scan field.
    /// The DASH7 registry is big-endian.
    scratch.ushort  = PLATFORM_ENDIAN16( vl_read(fp, (task->cursor)+=2 ) );
    sys_task_setnext(task, scratch.ushort);

    /// Advance cursor to next datum, go back to 0 if end of sequence
    /// (still works in special case where cursor = 254)
    task->cursor   += 2;
    task->cursor    = (task->cursor >= fp->length) ? 0 : task->cursor;
    vl_close(fp);

    /// Choosing Background-Scan or Foreground-Scan is based on scan-code.
    /// If b7 is set, do a Background-Scan.  At the session level, the "Flood"
    /// select uses b6, which is why there is a >> 1.
    s_new           = session_new(&dll_scan_applet, 0, s_channel,
                                    ((M2_NETSTATE_REQRX | M2_NETSTATE_INIT) | (s_code & 0x80) >> 1)  );
    s_new->extra    = s_code;
}
Ejemplo n.º 13
0
OT_WEAK void dll_refresh_rts(void) {
#if (OT_FEATURE(CRON) && M2_FEATURE(RTC_SCHEDULER))
///@todo need to update this implementation to also delete the old tasks.

    vlFILE* fp;

    // Early exit if RTS is not active
    if (dll.netconf.active_settings & M2_SET_SCHEDMASK) == 0)
        return;

    // - open the real-time-schedule ISF
    // - make sure the file is there
    // - make sure the file has at least 12 bytes (this is how much DLL uses)
    fp = ISF_open_su(ISF_ID_real_time_scheduler);
    if (fp != NULL) {
        if (fp->length < 12) {
            static const ot_task task_fn[3] = { &dll_systask_holdscan,
                                                &dll_systask_sleepscan,
                                                &dll_systask_beacon };

            ot_u16  sched_enable    = dll.netconf.active_settings & M2_SET_SCHEDMASK;
            ot_u16  cursor          = 0;
            ot_sigv* fn             = task_fn;

            // Apply Hold, Sleep, and Beacon RTS as specified in enabler bitmask
            while (sched_enable != 0) {
                if (sched_enable & M2_SET_HOLDSCHED) {
                    ot_u32 time_evt;
                    ot_u32 time_mask;
                    time_evt    = vl_read(fp, cursor);
                    time_mask   = 0xFFFF0000 | vl_read(fp, cursor+2);
                    otcron_add(*fn, CRONTIME_absolute, time_evt, time_mask);
                }
                fn++;
                cursor         += 4;
                sched_enable  <<= 1;
            }
        }
        vl_close(fp);
    }
#endif
}
Ejemplo n.º 14
0
ot_int sub_filedata( alp_tmpl* alp, id_tmpl* user_id, ot_u8 respond, ot_u8 cmd_in, ot_int data_in ) {
    vlFILE* fp;
    ot_u16  offset;
    ot_u16  span;
    ot_int  data_out    = 0;
    ot_bool inc_header  = (ot_bool)((cmd_in & 0x0F) == 0x0C);
    vlBLOCK file_block  = (vlBLOCK)((cmd_in >> 4) & 0x07);
    ot_u8   file_mod    = ((cmd_in & 0x02) ? VL_ACCESS_W : VL_ACCESS_R);
    ot_queue*  inq      = alp->inq;
    ot_queue*  outq     = alp->outq;
    
    sub_filedata_TOP:
    
    while (data_in > 0) {
        vaddr   header;
        ot_u8   err_code;
        ot_u8   file_id;
        ot_u16  limit;
        
        //alp->BOOKMARK_IN    = inq->getcursor;
        //alp->BOOKMARK_OUT   = NULL;
        
        file_id     = q_readbyte(inq);
        offset      = q_readshort(inq);
        span        = q_readshort(inq);
        limit       = offset + span;
        err_code    = vl_getheader_vaddr(&header, file_block, file_id, file_mod, user_id);
        file_mod    = ((file_mod & VL_ACCESS_W) != 0);
        //fp          = NULL;
        
        // A. File error catcher Stage
        // (In this case, gotos make it more readable)
        
        /// Make sure file header was retrieved properly, or goto error
        if (err_code != 0) {
            goto sub_filedata_senderror;
        }
        
        /// Make sure file opens properly, or goto error
        fp = vl_open_file(header);
        if (fp == NULL) {
            err_code = 0xFF;
            goto sub_filedata_senderror;
        }
        
        /// Make sure offset is within file bounds, or goto error
        if (offset >= fp->alloc) {
            err_code = 0x07;
            goto sub_filedata_senderror;
        }
        
        if (limit > fp->alloc) {
            limit       = fp->alloc;
            err_code    = 0x08;
        }

        // B. File Writing or Reading Stage
        // Write to file
        // 1. Process error on bad ALP parameters, but still do partial write
        // 2. offset, span are adjusted to convey leftover data
        // 3. miscellaneous write error occurs when vl_write fails
        if (file_mod) {
            for (; offset<limit; offset+=2, span-=2, data_in-=2) {
                if (inq->getcursor >= inq->back) {
                    goto sub_filedata_overrun;      
                }
                err_code |= vl_write(fp, offset, q_readshort_be(inq));
            }
        }
        
        // Read from File
        // 1. No error for bad read parameter, just fix the limit
        // 2. If inc_header param is set, include the file header in output
        // 3. Read out file data
        else {
            ot_u8 overhead;
            //limit       = (limit > fp->length) ? fp->length : limit;
            overhead    = 6;
            overhead   += (inc_header != 0) << 2; 
            
            if ((outq->putcursor+overhead) >= outq->back) {
                goto sub_filedata_overrun;
            }
            
            q_writeshort_be(outq, vworm_read(header + 4)); // id & mod
            if (inc_header) {
                q_writeshort(outq, vworm_read(header + 0));    // length
                q_writeshort(outq, vworm_read(header + 2));    // alloc
                data_out += 4;
            }
            q_writeshort(outq, offset);
            q_writeshort(outq, span);
            data_out += 6;
            
            for (; offset<limit; offset+=2, span-=2, data_out+=2) {
                if ((outq->putcursor+2) >= outq->back) {
                    goto sub_filedata_overrun;
                }
                q_writeshort_be(outq, vl_read(fp, offset));
            }
        }
        
        // C. Error Sending Stage
        sub_filedata_senderror:
        if ((respond != 0) && (err_code | file_mod)) {
            if ((outq->putcursor+2) >= outq->back) {
                goto sub_filedata_overrun;
            }
            q_writebyte(outq, file_id);
            q_writebyte(outq, err_code);
            q_markbyte(inq, span);         // go past any leftover input data
            data_out += 2;
        }
        
        data_in -= 5;   // 5 bytes input header
        vl_close(fp);
    }
    
    
    // Total Completion:
    // Set bookmark to NULL, because the record was completely processed
    //alp->BOOKMARK_IN = NULL;
    return data_out;
    
    
    // Partial or Non Completion:
    // Reconfigure last ALP operation, because it was not completely processed
    
    ///@todo Bookmarking is obsolete, because the way Chunking is done has
    /// been revised.  Chunked records must be contiguous.  ALP-Main will not
    /// call this app, and thus not call this function, until the message-end
    /// bit is detected, therefore meaning that all data is received and
    /// contiguous.  This overrun block, thus, should only check the flags for
    /// chunking, bypass them, and loop back to the top of this function.
    sub_filedata_overrun:
    vl_close(fp);
    
    ///@todo alp_next_chunk(alp);
    
//    {
//        ot_u8* scratch;
//        inq->getcursor  = (ot_u8*)alp->BOOKMARK_IN;
//        scratch         = inq->getcursor + 1;
//        *scratch++      = ((ot_u8*)&offset)[UPPER];
//        *scratch++      = ((ot_u8*)&offset)[LOWER];
//        *scratch++      = ((ot_u8*)&span)[UPPER];
//        *scratch        = ((ot_u8*)&span)[LOWER];
//    }
    
    return data_out;
}
Ejemplo n.º 15
0
void sub_build_uhfmsg(ot_int* buffer) {
/// This is the routine that builds the DASH7 UDP generic protocol message.
/// The protocol has data elements marked by a letter (T, V, R, E, D) that
/// signify Temperature, Voltage, RSSI (LF), PaLFi wake Event, and RX Data.
/// The elements are fixed/known length.
    session_tmpl    s_tmpl;
    command_tmpl    c_tmpl;
    ot_u8*          data_start;
    ot_u8           status;

    // Create a new session: you could change these parameters
    // Use "CHAN1" for odd events, "CHAN2" for even events
    s_tmpl.channel      = (palfi.wake_event & 1) ? ALERT_CHAN1 : ALERT_CHAN2;     
    s_tmpl.subnetmask   = 0;        // Use default subnet
    s_tmpl.flagmask     = 0;        // Use default app-flags
    s_tmpl.timeout      = 10;       // Do CSMA for no more than 10 ticks (~10 ms)
    otapi_new_session(&s_tmpl);
    
    // Broadcast request (takes no 2nd argument)
    otapi_open_request(ADDR_broadcast, NULL);
    
    // Insert Transport-Layer headers
    c_tmpl.type     = CMDTYPE_na2p_request;
    c_tmpl.opcode   = CMD_udp_on_file;
    c_tmpl.extension= CMDEXT_no_response;
    otapi_put_command_tmpl(&status, &c_tmpl);
    otapi_put_dialog_tmpl(&status, NULL);       // NULL = defaults
    
    // UDP Header
    q_writebyte(&txq, 255);        // Source Port: 255 (custom application port)
    q_writebyte(&txq, 255);        // Destination Port (same value)
    
    data_start = txq.putcursor;
    
    // Place temperature data
    q_writebyte(&txq, 'T');
    q_writeshort(&txq, buffer[0]);
    
    // Place Voltage data
    q_writebyte(&txq, 'V');
    q_writeshort(&txq, buffer[1]);
    
    // Place RSSI data
    q_writebyte(&txq, 'R');
    q_writestring(&txq, (ot_u8*)&palfi.rssi1, 3);

    // Place Action data
    q_writebyte(&txq, 'E');
    q_writebyte(&txq, (ot_int)palfi.wake_event);
    
    // Dump some received data
    if (palfi.wake_event) {
        q_writebyte(&txq, 'D');
        q_writestring(&txq, palfi.rxdata, 8);
    }
    
    // Store this information into the Port 255 file for continuous, automated
    // reporting by DASH7/OpenTag until it is updated next time.  The length of 
    // this information is always 23 bytes.
    {
    	vlFILE* fp;
    	fp = ISF_open_su(255);
    	if (fp != NULL) {
    		vl_store(fp, 23, data_start);
    		vl_close(fp);
    	}
    }
    
    // Finish Message
    otapi_close_request();
}