2.1.28, initiation update to PXT v5.28.24 (#54)

This commit is contained in:
Amerlander
2019-12-02 05:58:26 +01:00
committed by Peli de Halleux
parent 38a964516e
commit 5c114a0c57
1261 changed files with 50692 additions and 21604 deletions

View File

@ -1,23 +0,0 @@
{
"radio": "Daten mithilfe von Funk-Paketen übertragen",
"radio.onDataPacketReceived": "Registriert Funktionen, die ausgeführt werden, wenn das Radio ein Datenpaket empfängt. Entnimmt das empfangene Paket aus der Warteschlange des Radios.",
"radio.onDataReceived": "Registriert Code der ausgeführt wird, wenn ein Paket über Funk empfangen wird.",
"radio.receiveNumber": "Liest das nächste Paket aus der Funk-Warteschlange und gibt die Paketnummer oder 0 aus, wenn das Paket keine Nummer enthält.",
"radio.receiveString": "Liest das nächste Paket aus der Funk-Warteschlange und gibt die enthaltene Zeichenfolge oder die leere Zeichenfolge aus, wenn das Paket keine Zeichenfolge enthält.",
"radio.receivedNumber": "Extrahiert eine Zahl aus dem Inhalt des letzten Datenpakets, welches aus der Warteschlange des Radios (via ``Zahl empfangen``, ``Zeichenkette empfangen``, etc) entnommen wurde oder eine 0, wenn das Paket keine Zahl enthält.",
"radio.receivedSerial": "Extrahiert die Seriennummer des Calliope mini Senders aus dem Inhalt des letzten Datenpakets, welches aus der Warteschlange des Radios entnommen wurde oder eine 0, wenn der Absender keine Seriennummer gesendet hat.",
"radio.receivedSignalStrength": "Ruft den empfangenen Signalstärkeindikator (RSSI) aus dem letzten Paket aus der Funk-Warteschlange aus (via ``receiveNumber``, ``receiveString``, etc). Wird im Simulator nicht unterstützt.\nnamespace=radio",
"radio.receivedString": "Extrahiert die Zeichenkette aus dem Inhalt des letzten Datenpakets, welches aus der Warteschlange des Radios (via ``Zahl empfangen``, ``Zeichenkette empfangen``, etc) entnommen wurde oder eine leere Zeichenkette, wenn das Paket keine Zeichenkette enthält.",
"radio.receivedTime": "Extrahiert die Systemzeit des Absenders aus dem Inhalt des letzten Datenpakets, welches aus der Warteschlange des Radios (via ``Zahl empfangen``, ``Zeichenkette empfangen``, etc) entnommen wurde.",
"radio.sendNumber": "Überträgt eine Nummer über Funk an jeden angeschlossenen mini in der Gruppe.",
"radio.sendString": "Überträgt eine Zeichenfolge über Funk mit Seriennummer des Geräts und Laufzeit an jeden angeschlossenen mini in der Gruppe.",
"radio.sendValue": "Sendet ein Name / Wert-Paar zusammen mit der Seriennummer des Geräts und die Laufzeit auf alle angeschlossenen minis in der Gruppe.",
"radio.sendValue|param|value": "der numerische Wert",
"radio.setGroup": "Setzt die Gruppen-ID für Funkverbindungen. Ein mini kann nur ein Gruppen-ID hören.\nDie Gruppen-ID zwischen liegt zwischen 0 und 255, z.B. 1",
"radio.setTransmitPower": "Ändere die Ausgabeleistung des Senders auf den angegebenen Wert.",
"radio.setTransmitPower|param|power": "ein Wert im Bereich 0.. 7, wo 0 die niedrigste Leistung und 7 ist ist die höchste. z.B. 7",
"radio.setTransmitSerialNumber": "Stelle den Dunk so ein, dass die Seriennummer in jeder Nachricht übertragen wird.",
"radio.setTransmitSerialNumber|param|transmit": "Wert, der anzeigt, ob die Seriennummer übertragen wird, z.B. wahr",
"radio.writeReceivedPacketToSerial": "Schreibt das letzte empfangene Paket als JSON auf Seriell. Sollte in einem ´´onDataPacketReceived``-Callback aufgerufen werden.",
"radio.writeValueToSerial": "Liest das nächste Paket aus der Funk-Warteschlange und schreibt dieses als JSON auf Seriell."
}

View File

@ -1,17 +0,0 @@
{
"radio.onDataPacketReceived|block": "wenn Datenpaket empfangen",
"radio.onDataReceived|block": "Funk auf empfangenen Daten",
"radio.receiveNumber|block": "empfange Nummer über Funk",
"radio.receiveString|block": "empfange Zeichenfolge über Funk",
"radio.receivedSignalStrength|block": "über Funk empfangene Signalstärke",
"radio.sendNumber|block": "sende Nummer %value über Funk",
"radio.sendString|block": "sende Zeichenfolge %msg über Funk",
"radio.sendValue|block": "schicke |Wert %name|= %value über Funk",
"radio.setGroup|block": "setze Gruppe %ID über Funk",
"radio.setTransmitPower|block": "setze Übertragungsstärke %power über Funk",
"radio.setTransmitSerialNumber|block": "setze Übertragungsseriennummer %transmit über Funk",
"radio.writeReceivedPacketToSerial|block": "schreibe das über Funk übertragene Paket auf Seriell",
"radio.writeValueToSerial|block": "schreibe Wert über Funk auf Seriell",
"radio|block": "Funk",
"{id:category}Radio": "Funk"
}

View File

@ -1,25 +1,46 @@
{
"radio": "Communicate data using radio packets",
"radio.Packet.receivedBuffer": "The buffer payload if a buffer was sent in this packet\nor the empty buffer",
"radio.Packet.receivedNumber": "The number payload if a number was sent in this packet (via ``sendNumber()`` or ``sendValue()``)\nor 0 if this packet did not contain a number.",
"radio.Packet.receivedString": "The string payload if a string was sent in this packet (via ``sendString()`` or ``sendValue()``)\nor the empty string if this packet did not contain a string.",
"radio.Packet.serial": "The serial number of the sender of the packet or 0 if the sender did not sent their serial number.",
"radio.Packet.signal": "The received signal strength indicator (RSSI) of the packet.",
"radio.Packet.time": "The system time of the sender of the packet at the time the packet was sent.",
"radio.onDataPacketReceived": "Registers code to run when the radio receives a packet. Also takes the\nreceived packet from the radio queue.",
"radio._packetProperty": "Gets a packet property.",
"radio._packetProperty|param|type": "the packet property type, eg: PacketProperty.time",
"radio.onDataPacketReceived": "Deprecated. Use onDataReceived() instead\nRegisters code to run when the radio receives a packet. Also takes the\nreceived packet from the radio queue.",
"radio.onDataReceived": "Registers code to run when a packet is received over radio.",
"radio.onReceivedBuffer": "Registers code to run when the radio receives a buffer.",
"radio.onReceivedBufferDeprecated": "Registers code to run when the radio receives a buffer. Deprecated, use\nonReceivedBuffer instead.",
"radio.onReceivedNumber": "Registers code to run when the radio receives a number.",
"radio.onReceivedNumberDeprecated": "Registers code to run when the radio receives a number. Deprecated, use\nonReceivedNumber instead.",
"radio.onReceivedString": "Registers code to run when the radio receives a string.",
"radio.onReceivedStringDeprecated": "Registers code to run when the radio receives a string. Deprecated, use\nonReceivedString instead.",
"radio.onReceivedValue": "Registers code to run when the radio receives a key value pair.",
"radio.onReceivedValueDeprecated": "Registers code to run when the radio receives a key value pair. Deprecated, use\nonReceivedValue instead.",
"radio.raiseEvent": "Sends an event over radio to neigboring devices",
"radio.readRawPacket": "Internal use only. Takes the next packet from the radio queue and returns its contents + RSSI in a Buffer",
"radio.receiveNumber": "Reads the next packet from the radio queue and returns the packet's number\npayload or 0 if the packet did not contain a number.",
"radio.receiveString": "Reads the next packet from the radio queue and returns the packet's string\npayload or the empty string if the packet did not contain a string.",
"radio.receivedBuffer": "Returns the buffer payload from the last packet taken from the radio queue\n(via ``receiveNumber``, ``receiveString``, etc) or the empty string if that\npacket did not contain a string.",
"radio.receivedNumber": "Returns the number payload from the last packet taken from the radio queue\n(via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not\ncontain a number.",
"radio.receivedPacket": "Returns properties of the last radio packet received.",
"radio.receivedPacket|param|type": "the type of property to retrieve from the last packet",
"radio.receivedSerial": "Returns the serial number of the sender micro:bit from the last packet taken\nfrom the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if\nthat packet did not send a serial number.",
"radio.receivedSignalStrength": "Gets the received signal strength indicator (RSSI) from the last packet taken\nfrom the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.\nnamespace=radio",
"radio.receivedSignalStrength": "Gets the received signal strength indicator (RSSI) from the last packet taken\nfrom the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.",
"radio.receivedString": "Returns the string payload from the last packet taken from the radio queue\n(via ``receiveNumber``, ``receiveString``, etc) or the empty string if that\npacket did not contain a string.",
"radio.receivedTime": "Returns the system time of the sender micro:bit at the moment when it sent the\nlast packet taken from the radio queue (via ``receiveNumber``,\n``receiveString``, etc).",
"radio.sendBuffer": "Broadcasts a buffer (up to 19 bytes long) along with the device serial number\nand running time to any connected micro:bit in the group.",
"radio.sendNumber": "Broadcasts a number over radio to any connected micro:bit in the group.",
"radio.sendRawPacket": "Internal use only. Sends a raw packet through the radio (assumes RSSI appened to packet)",
"radio.sendString": "Broadcasts a string along with the device serial number\nand running time to any connected micro:bit in the group.",
"radio.sendValue": "Broadcasts a name / value pair along with the device serial number\nand running time to any connected micro:bit in the group.",
"radio.sendValue|param|name": "the field name (max 12 characters), eg: \"name\"",
"radio.sendValue|param|value": "the numberic value",
"radio.setGroup": "Sets the group id for radio communications. A micro:bit can only listen to one group ID at any time.\n@ param id the group id between ``0`` and ``255``, 1 eg",
"radio.sendValue": "Broadcasts a name / value pair along with the device serial number\nand running time to any connected micro:bit in the group. The name can\ninclude no more than 8 characters.",
"radio.sendValue|param|name": "the field name (max 8 characters), eg: \"name\"",
"radio.sendValue|param|value": "the numeric value",
"radio.setFrequencyBand": "Change the transmission and reception band of the radio to the given channel",
"radio.setFrequencyBand|param|band": "a frequency band in the range 0 - 83. Each step is 1MHz wide, based at 2400MHz.",
"radio.setGroup": "Sets the group id for radio communications. A micro:bit can only listen to one group ID at any time.",
"radio.setGroup|param|id": "the group id between ``0`` and ``255``, eg: 1",
"radio.setTransmitPower": "Change the output power level of the transmitter to the given value.",
"radio.setTransmitPower|param|power": "a value in the range 0..7, where 0 is the lowest power and 7 is the highest. eg: 7",
"radio.setTransmitSerialNumber": "Set the radio to transmit the serial number in each message.",

View File

@ -1,12 +1,27 @@
{
"RadioPacketProperty.SerialNumber|block": "serial number",
"RadioPacketProperty.SignalStrength|block": "signal strength",
"RadioPacketProperty.Time|block": "time",
"radio._packetProperty|block": "%note",
"radio.onDataPacketReceived|block": "on radio received",
"radio.onDataReceived|block": "radio on data received",
"radio.onReceivedBufferDeprecated|block": "on radio received",
"radio.onReceivedBuffer|block": "on radio received",
"radio.onReceivedNumberDeprecated|block": "on radio received",
"radio.onReceivedNumber|block": "on radio received",
"radio.onReceivedStringDeprecated|block": "on radio received",
"radio.onReceivedString|block": "on radio received",
"radio.onReceivedValueDeprecated|block": "on radio received",
"radio.onReceivedValue|block": "on radio received",
"radio.raiseEvent|block": "radio raise event|from source %src=control_event_source_id|with value %value=control_event_value_id",
"radio.receiveNumber|block": "radio receive number",
"radio.receiveString|block": "radio receive string",
"radio.receivedPacket|block": "received packet %type=radio_packet_property",
"radio.receivedSignalStrength|block": "radio received signal strength",
"radio.sendNumber|block": "radio send number %value",
"radio.sendString|block": "radio send string %msg",
"radio.sendValue|block": "radio send|value %name|= %value",
"radio.setFrequencyBand|block": "radio set frequency band %band",
"radio.setGroup|block": "radio set group %ID",
"radio.setTransmitPower|block": "radio set transmit power %power",
"radio.setTransmitSerialNumber|block": "radio set transmit serial number %transmit",

188
libs/radio/deprecated.ts Normal file
View File

@ -0,0 +1,188 @@
namespace radio {
export class Packet {
/**
* The number payload if a number was sent in this packet (via ``sendNumber()`` or ``sendValue()``)
* or 0 if this packet did not contain a number.
*/
public receivedNumber: number;
/**
* The string payload if a string was sent in this packet (via ``sendString()`` or ``sendValue()``)
* or the empty string if this packet did not contain a string.
*/
public receivedString: string;
/**
* The buffer payload if a buffer was sent in this packet
* or the empty buffer
*/
public receivedBuffer: Buffer;
/**
* The system time of the sender of the packet at the time the packet was sent.
*/
public time: number;
/**
* The serial number of the sender of the packet or 0 if the sender did not sent their serial number.
*/
public serial: number;
/**
* The received signal strength indicator (RSSI) of the packet.
*/
public signal: number;
}
/**
* Deprecated. Use onDataReceived() instead
* Registers code to run when the radio receives a packet. Also takes the
* received packet from the radio queue.
*/
//% help=radio/on-data-packet-received blockHandlerKey="radioreceived" deprecated=true
//% mutate=objectdestructuring
//% mutateText=Packet
//% mutateDefaults="receivedNumber;receivedString:name,receivedNumber:value;receivedString"
//% blockId=radio_on_packet block="on radio received" blockGap=8
export function onDataPacketReceived(cb: (packet: Packet) => void) {
onDataReceived(() => {
receiveNumber();
const packet = new Packet();
packet.receivedNumber = receivedNumber();
packet.time = receivedTime();
packet.serial = receivedSerial();
packet.receivedString = receivedString();
packet.receivedBuffer = receivedBuffer();
packet.signal = receivedSignalStrength();
cb(packet)
});
}
/**
* Registers code to run when the radio receives a number. Deprecated, use
* onReceivedNumber instead.
*/
//% help=radio/on-received-number blockHandlerKey="radioreceived"
//% blockId=radio_on_number block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" deprecated=1
export function onReceivedNumberDeprecated(cb: (receivedNumber: number) => void) {
onReceivedNumber(cb);
}
/**
* Registers code to run when the radio receives a key value pair. Deprecated, use
* onReceivedValue instead.
*/
//% help=radio/on-received-value blockHandlerKey="radioreceived"
//% blockId=radio_on_value block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" deprecated=1
export function onReceivedValueDeprecated(cb: (name: string, value: number) => void) {
onReceivedValue(cb);
}
/**
* Registers code to run when the radio receives a string. Deprecated, use
* onReceivedString instead.
*/
//% help=radio/on-received-string blockHandlerKey="radioreceived"
//% blockId=radio_on_string block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" deprecated=1
export function onReceivedStringDeprecated(cb: (receivedString: string) => void) {
onReceivedString(cb);
}
/**
* Registers code to run when the radio receives a buffer. Deprecated, use
* onReceivedBuffer instead.
*/
//% help=radio/on-received-buffer blockHandlerKey="radioreceived" blockHidden=1
//% blockId=radio_on_buffer block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" deprecated=1
export function onReceivedBufferDeprecated(cb: (receivedBuffer: Buffer) => void) {
onReceivedBuffer(cb);
}
/**
* Reads the next packet from the radio queue and and writes it to serial
* as JSON.
*/
//% help=radio/write-value-to-serial
//% weight=3
//% blockId=radio_write_value_serial block="radio write value to serial"
//% deprecated=true
export function writeValueToSerial() {
const p = RadioPacket.getPacket(radio.readRawPacket());
writeToSerial(p);
}
/**
* Returns the number payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
* contain a number.
*/
//% help=radio/received-number deprecated=1
export function receivedNumber(): number {
return (lastPacket ? lastPacket.numberPayload : 0) || 0;
}
/**
* Returns the serial number of the sender micro:bit from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if
* that packet did not send a serial number.
*/
//% help=radio/received-serial deprecated=1
export function receivedSerial(): number {
return lastPacket ? lastPacket.serial : 0;
}
/**
* Returns the string payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
* packet did not contain a string.
*/
//% help=radio/received-string deprecated=1
export function receivedString(): string {
return (lastPacket ? lastPacket.stringPayload : "") || "";
}
/**
* Returns the buffer payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
* packet did not contain a string.
*/
//% help=radio/received-buffer deprecated=1
export function receivedBuffer(): Buffer {
return (lastPacket ? lastPacket.bufferPayload : null) || control.createBuffer(0);
}
/**
* Returns the system time of the sender micro:bit at the moment when it sent the
* last packet taken from the radio queue (via ``receiveNumber``,
* ``receiveString``, etc).
*/
//% help=radio/received-time deprecated=1
export function receivedTime(): number {
return lastPacket ? lastPacket.time : 0;
}
/**
* Reads the next packet from the radio queue and returns the packet's number
* payload or 0 if the packet did not contain a number.
*/
//% help=radio/receive-number
//% weight=46
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8
//% deprecated=true
export function receiveNumber(): number {
lastPacket = RadioPacket.getPacket(readRawPacket());
return receivedNumber();
}
/**
* Reads the next packet from the radio queue and returns the packet's string
* payload or the empty string if the packet did not contain a string.
*/
//% blockId=radio_datagram_receive_string block="radio receive string" blockGap=8
//% weight=44
//% help=radio/receive-string
//% deprecated=true
export function receiveString(): string {
lastPacket = RadioPacket.getPacket(readRawPacket());
return receivedString();
}
}

View File

@ -7,9 +7,9 @@
"enums.d.ts",
"radio.cpp",
"radio.ts",
"_locales/de/radio-jsdoc-strings.json",
"_locales/de/radio-strings.json"
"deprecated.ts"
],
"icon": "./static/packages/radio/icon.png",
"public": true,
"dependencies": {
"core": "file:../core"

View File

@ -2,44 +2,10 @@
using namespace pxt;
#define MAX_FIELD_NAME_LENGTH 12
#define MAX_PAYLOAD_LENGTH 20
#define PACKET_PREFIX_LENGTH 9
#define VALUE_PACKET_NAME_LEN_OFFSET 13
// Packet Spec:
// | 0 | 1 ... 4 | 5 ... 8 | 9 ... 28
// ----------------------------------------------------------------
// | packet type | system time | serial number | payload
//
// Serial number defaults to 0 unless enabled by user
// payload: number (9 ... 12)
#define PACKET_TYPE_NUMBER 0
// payload: number (9 ... 12), name length (13), name (14 ... 26)
#define PACKET_TYPE_VALUE 1
// payload: string length (9), string (10 ... 28)
#define PACKET_TYPE_STRING 2
//% color=270 weight=34
//% color=#E3008C weight=96 icon="\uf012"
namespace radio {
// -------------------------------------------------------------------------
// Radio
// -------------------------------------------------------------------------
bool radioEnabled = false;
bool transmitSerialNumber = false;
PacketBuffer packet;
uint8_t type;
uint32_t time;
uint32_t serial;
int value;
StringData* msg;
int radioEnable() {
int r = uBit.radio.enable();
@ -49,224 +15,56 @@ namespace radio {
}
if (!radioEnabled) {
uBit.radio.setGroup(pxt::programHash());
uBit.radio.setTransmitPower(6); // start with high power by default
radioEnabled = true;
}
return r;
}
void broadcastMessage(int message) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.event.eventReceived(MicroBitEvent(MES_BROADCAST_GENERAL_ID, message, CREATE_ONLY));
}
void onBroadcastMessageReceived(int message, Action f) {
if (radioEnable() != MICROBIT_OK) return;
registerWithDal(MES_BROADCAST_GENERAL_ID, message, f);
}
void setPacketPrefix(uint8_t* buf, int type) {
// prefix: type (0), time (1..4), serial (5..8)
uint32_t t = system_timer_current_time();
uint32_t sn = transmitSerialNumber ? microbit_serial_number() : 0;
buf[0] = (uint8_t) type;
memcpy(buf + 1, &t, 4);
memcpy(buf + 5, &sn, 4);
}
uint8_t copyStringValue(uint8_t* buf, StringData* data, uint8_t maxLength) {
ManagedString s(data);
uint8_t len = min(maxLength, s.length());
// One byte for length of the string
buf[0] = len;
if (len > 0) {
memcpy(buf + 1, s.toCharArray(), len);
}
return len + 1;
}
StringData* getStringValue(uint8_t* buf, uint8_t maxLength) {
// First byte is the string length
uint8_t len = min(maxLength, buf[0]);
if (len) {
char name[maxLength + 1];
memcpy(name, buf + 1, len);
name[len] = 0;
return ManagedString(name).leakData();
}
return ManagedString().leakData();
}
void writePacketAsJSON(uint8_t tp, int v, int s, int t, StringData* m) {
// Convert the packet to JSON and send over serial
uBit.serial.send("{");
uBit.serial.send("\"t\":");
uBit.serial.send(t);
uBit.serial.send(",\"s\":");
uBit.serial.send(s);
if (tp == PACKET_TYPE_STRING || tp == PACKET_TYPE_VALUE) {
uBit.serial.send(",\"n\":\"");
uBit.serial.send(m);
uBit.serial.send("\"");
}
if (tp == PACKET_TYPE_NUMBER || tp == PACKET_TYPE_VALUE) {
uBit.serial.send(",\"v\":");
uBit.serial.send(v);
}
uBit.serial.send("}\r\n");
}
/**
* Takes a packet from the micro:bit radio queue.
* @param writeToSerial if true, write the received packet to serial without updating the global packet;
if false, update the global packet instead
*/
void receivePacket(bool writeToSerial) {
PacketBuffer p = uBit.radio.datagram.recv();
uint8_t* buf = p.getBytes();
uint8_t tp;
int t;
int s;
int v;
StringData* m;
memcpy(&tp, buf, 1);
memcpy(&t, buf + 1, 4);
memcpy(&s, buf + 5, 4);
if (tp == PACKET_TYPE_STRING) {
v = 0;
m = getStringValue(buf + PACKET_PREFIX_LENGTH, MAX_PAYLOAD_LENGTH - 1);
}
else {
memcpy(&v, buf + 9, 4);
if (tp == PACKET_TYPE_VALUE) {
m = getStringValue(buf + VALUE_PACKET_NAME_LEN_OFFSET, MAX_FIELD_NAME_LENGTH);
}
else {
m = ManagedString().leakData();
}
}
if (!writeToSerial) {
// Refresh global packet
packet = p;
type = tp;
time = t;
serial = s;
value = v;
msg = m;
}
else {
writePacketAsJSON(tp, v, s, t, m);
}
}
/**
* Broadcasts a number over radio to any connected micro:bit in the group.
*/
//% help=radio/send-number
//% weight=60
//% blockId=radio_datagram_send block="radio send number %value" blockGap=8
void sendNumber(int value) {
if (radioEnable() != MICROBIT_OK) return;
uint8_t length = PACKET_PREFIX_LENGTH + sizeof(uint32_t);
uint8_t buf[length];
memset(buf, 0, length);
setPacketPrefix(buf, PACKET_TYPE_NUMBER);
memcpy(buf + PACKET_PREFIX_LENGTH, &value, 4);
uBit.radio.datagram.send(buf, length);
}
/**
* Broadcasts a name / value pair along with the device serial number
* and running time to any connected micro:bit in the group.
* @param name the field name (max 12 characters), eg: "name"
* @param value the numberic value
* Sends an event over radio to neigboring devices
*/
//% help=radio/send-value
//% weight=59
//% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8
void sendValue(StringData* name, int value) {
if (radioEnable() != MICROBIT_OK) return;
ManagedString n(name);
uint8_t buf[32];
memset(buf, 0, 32);
setPacketPrefix(buf, PACKET_TYPE_VALUE);
memcpy(buf + PACKET_PREFIX_LENGTH, &value, 4);
int stringLen = copyStringValue(buf + VALUE_PACKET_NAME_LEN_OFFSET, name, MAX_FIELD_NAME_LENGTH);
uBit.radio.datagram.send(buf, VALUE_PACKET_NAME_LEN_OFFSET + stringLen);
}
/**
* Broadcasts a string along with the device serial number
* and running time to any connected micro:bit in the group.
*/
//% help=radio/send-string
//% weight=58
//% blockId=radio_datagram_send_string block="radio send string %msg"
void sendString(StringData* msg) {
if (radioEnable() != MICROBIT_OK) return;
uint8_t buf[32];
memset(buf, 0, 32);
setPacketPrefix(buf, PACKET_TYPE_STRING);
int stringLen = copyStringValue(buf + PACKET_PREFIX_LENGTH, msg, MAX_PAYLOAD_LENGTH - 1);
uBit.radio.datagram.send(buf, PACKET_PREFIX_LENGTH + stringLen);
}
/**
* Reads the next packet from the radio queue and and writes it to serial
* as JSON.
*/
//% help=radio/write-value-to-serial
//% weight=3
//% blockId=radio_write_value_serial block="radio write value to serial"
//% deprecated=true
void writeValueToSerial() {
if (radioEnable() != MICROBIT_OK) return;
receivePacket(true);
}
/**
* Writes the last received packet to serial as JSON. This should be called
* within an ``onDataPacketReceived`` callback.
*/
//% help=radio/write-received-packet-to-serial
//% weight=3
//% blockId=radio_write_packet_serial block="radio write received packet to serial"
//% blockId=radioRaiseEvent block="radio raise event|from source %src=control_event_source_id|with value %value=control_event_value_id"
//% blockExternalInputs=1
//% advanced=true
void writeReceivedPacketToSerial() {
//% weight=1
//% help=radio/raise-event
void raiseEvent(int src, int value) {
if (radioEnable() != MICROBIT_OK) return;
writePacketAsJSON(type, value, (int) serial, (int) time, msg);
uBit.radio.event.eventReceived(MicroBitEvent(src, value, CREATE_ONLY));
}
/**
* Reads the next packet from the radio queue and returns the packet's number
* payload or 0 if the packet did not contain a number.
* Internal use only. Takes the next packet from the radio queue and returns its contents + RSSI in a Buffer
*/
//% help=radio/receive-number
//% weight=46
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8
//% deprecated=true
int receiveNumber()
{
if (radioEnable() != MICROBIT_OK) return 0;
receivePacket(false);
return value;
//%
Buffer readRawPacket() {
if (radioEnable() != MICROBIT_OK) return mkBuffer(NULL, 0);
PacketBuffer p = uBit.radio.datagram.recv();
if (p == PacketBuffer::EmptyPacket)
return mkBuffer(NULL, 0);
int rssi = p.getRSSI();
uint8_t buf[MICROBIT_RADIO_MAX_PACKET_SIZE + sizeof(int)]; // packet length + rssi
memset(buf, 0, sizeof(buf));
memcpy(buf, p.getBytes(), p.length()); // data
memcpy(buf + MICROBIT_RADIO_MAX_PACKET_SIZE, &rssi, sizeof(int)); // RSSi - assumes Int32LE layout
return mkBuffer(buf, sizeof(buf));
}
/**
* Internal use only. Sends a raw packet through the radio (assumes RSSI appened to packet)
*/
//% async
void sendRawPacket(Buffer msg) {
if (radioEnable() != MICROBIT_OK || NULL == msg) return;
// don't send RSSI data; and make sure no buffer underflow
int len = msg->length - sizeof(int);
if (len > 0)
uBit.radio.datagram.send(msg->data, len);
}
/**
@ -278,49 +76,22 @@ namespace radio {
//% deprecated=true
void onDataReceived(Action body) {
if (radioEnable() != MICROBIT_OK) return;
registerWithDal(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, body);
// make sure the receive buffer has a free spot
receiveNumber();
}
/**
* Reads the next packet from the radio queue and returns the packet's string
* payload or the empty string if the packet did not contain a string.
*/
//% blockId=radio_datagram_receive_string block="radio receive string" blockGap=8
//% weight=44
//% help=radio/receive-string
//% deprecated=true
StringData* receiveString() {
if (radioEnable() != MICROBIT_OK) return ManagedString().leakData();
receivePacket(false);
return msg;
}
/**
* Gets the received signal strength indicator (RSSI) from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
* namespace=radio
*/
//% help=radio/received-signal-strength
//% weight=40
//% blockId=radio_datagram_rssi block="radio received signal strength"
//% deprecated=true
int receivedSignalStrength() {
if (radioEnable() != MICROBIT_OK) return 0;
return packet.getRSSI();
uBit.radio.datagram.recv(); // wake up read code
}
/**
* Sets the group id for radio communications. A micro:bit can only listen to one group ID at any time.
* @ param id the group id between ``0`` and ``255``, 1 eg
* @param id the group id between ``0`` and ``255``, eg: 1
*/
//% help=radio/set-group
//% weight=10 blockGap=8 advanced=true
//% weight=100
//% blockId=radio_set_group block="radio set group %ID"
//% id.min=0 id.max=255
void setGroup(int id) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.setGroup(id);
}
@ -331,66 +102,25 @@ namespace radio {
//% help=radio/set-transmit-power
//% weight=9 blockGap=8
//% blockId=radio_set_transmit_power block="radio set transmit power %power"
//% power.min=0 power.max=7
//% advanced=true
void setTransmitPower(int power) {
if (radioEnable() != MICROBIT_OK) return;
uBit.radio.setTransmitPower(power);
}
/**
* Set the radio to transmit the serial number in each message.
* @param transmit value indicating if the serial number is transmitted, eg: true
*/
//% help=radio/set-transmit-serial-number
* Change the transmission and reception band of the radio to the given channel
* @param band a frequency band in the range 0 - 83. Each step is 1MHz wide, based at 2400MHz.
**/
//% help=radio/set-frequency-band
//% weight=8 blockGap=8
//% blockId=radio_set_transmit_serial_number block="radio set transmit serial number %transmit"
//% blockId=radio_set_frequency_band block="radio set frequency band %band"
//% band.min=0 band.max=83
//% advanced=true
void setTransmitSerialNumber(bool transmit) {
void setFrequencyBand(int band) {
if (radioEnable() != MICROBIT_OK) return;
transmitSerialNumber = transmit;
}
/**
* Returns the number payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
* contain a number.
*/
//% help=radio/received-number
int receivedNumber() {
if (radioEnable() != MICROBIT_OK) return 0;
return value;
}
/**
* Returns the serial number of the sender micro:bit from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if
* that packet did not send a serial number.
*/
//% help=radio/received-serial
uint32_t receivedSerial() {
if (radioEnable() != MICROBIT_OK) return 0;
return serial;
}
/**
* Returns the string payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
* packet did not contain a string.
*/
//% help=radio/received-string
StringData* receivedString() {
if (radioEnable() != MICROBIT_OK) return ManagedString().leakData();
return msg;
}
/**
* Returns the system time of the sender micro:bit at the moment when it sent the
* last packet taken from the radio queue (via ``receiveNumber``,
* ``receiveString``, etc).
*/
//% help=radio/received-time
uint32_t receivedTime() {
if (radioEnable() != MICROBIT_OK) return 0;
return time;
uBit.radio.setFrequencyBand(band);
}
}

View File

@ -1,52 +1,444 @@
enum RadioPacketProperty {
//% blockIdentity=radio._packetProperty
//% block="signal strength"
SignalStrength = 2,
//% blockIdentity=radio._packetProperty
//% block="time"
Time = 0,
//% block="serial number"
//% blockIdentity=radio._packetProperty
SerialNumber = 1
}
/**
* Communicate data using radio packets
*/
//% color=#E3008C weight=34 icon="\uf012"
//% color=#E3008C weight=96 icon="\uf012"
namespace radio {
export class Packet {
/**
* The number payload if a number was sent in this packet (via ``sendNumber()`` or ``sendValue()``)
* or 0 if this packet did not contain a number.
*/
public receivedNumber: number;
/**
* The string payload if a string was sent in this packet (via ``sendString()`` or ``sendValue()``)
* or the empty string if this packet did not contain a string.
*/
public receivedString: string;
/**
* The system time of the sender of the packet at the time the packet was sent.
*/
public time: number;
/**
* The serial number of the sender of the packet or 0 if the sender did not sent their serial number.
*/
public serial: number;
/**
* The received signal strength indicator (RSSI) of the packet.
*/
public signal: number;
const MAX_FIELD_DOUBLE_NAME_LENGTH = 8;
const MAX_PAYLOAD_LENGTH = 20;
const PACKET_PREFIX_LENGTH = 9;
const VALUE_PACKET_NAME_LEN_OFFSET = 13;
const DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET = 17;
// Packet Spec:
// | 0 | 1 ... 4 | 5 ... 8 | 9 ... 28
// ----------------------------------------------------------------
// | packet type | system time | serial number | payload
//
// Serial number defaults to 0 unless enabled by user
// payload: number (9 ... 12)
const PACKET_TYPE_NUMBER = 0;
// payload: number (9 ... 12), name length (13), name (14 ... 26)
const PACKET_TYPE_VALUE = 1;
// payload: string length (9), string (10 ... 28)
const PACKET_TYPE_STRING = 2;
// payload: buffer length (9), buffer (10 ... 28)
const PACKET_TYPE_BUFFER = 3;
// payload: number (9 ... 16)
const PACKET_TYPE_DOUBLE = 4;
// payload: number (9 ... 16), name length (17), name (18 ... 26)
const PACKET_TYPE_DOUBLE_VALUE = 5;
let transmittingSerial: boolean;
let initialized = false;
export let lastPacket: RadioPacket;
let onReceivedNumberHandler: (receivedNumber: number) => void;
let onReceivedValueHandler: (name: string, value: number) => void;
let onReceivedStringHandler: (receivedString: string) => void;
let onReceivedBufferHandler: (receivedBuffer: Buffer) => void;
function init() {
if (initialized) return;
initialized = true;
onDataReceived(handleDataReceived);
}
function handleDataReceived() {
let buffer: Buffer = readRawPacket();
while (buffer && buffer.length) {
lastPacket = RadioPacket.getPacket(buffer);
switch (lastPacket.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_DOUBLE:
if (onReceivedNumberHandler)
onReceivedNumberHandler(lastPacket.numberPayload);
break;
case PACKET_TYPE_VALUE:
case PACKET_TYPE_DOUBLE_VALUE:
if (onReceivedValueHandler)
onReceivedValueHandler(lastPacket.stringPayload, lastPacket.numberPayload);
break;
case PACKET_TYPE_BUFFER:
if (onReceivedBufferHandler)
onReceivedBufferHandler(lastPacket.bufferPayload);
break;
case PACKET_TYPE_STRING:
if (onReceivedStringHandler)
onReceivedStringHandler(lastPacket.stringPayload);
break;
}
// read next packet if any
buffer = readRawPacket();
}
}
/**
* Registers code to run when the radio receives a packet. Also takes the
* received packet from the radio queue.
* Registers code to run when the radio receives a number.
*/
//% help=radio/on-data-packet-received
//% mutate=objectdestructuring
//% mutateText=Packet
//% mutateDefaults="receivedNumber;receivedString:name,receivedNumber:value;receivedString"
//% blockId=radio_on_packet block="on radio received" blockGap=8
export function onDataPacketReceived(cb: (packet: Packet) => void) {
onDataReceived(() => {
receiveNumber();
const packet = new Packet();
packet.receivedNumber = receivedNumber();
packet.time = receivedTime();
packet.serial = receivedSerial();
packet.receivedString = receivedString();
packet.signal = receivedSignalStrength();
cb(packet)
});
//% help=radio/on-received-number
//% blockId=radio_on_number_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedNumber(cb: (receivedNumber: number) => void) {
init();
onReceivedNumberHandler = cb;
}
}
/**
* Registers code to run when the radio receives a key value pair.
*/
//% help=radio/on-received-value
//% blockId=radio_on_value_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedValue(cb: (name: string, value: number) => void) {
init();
onReceivedValueHandler = cb;
}
/**
* Registers code to run when the radio receives a string.
*/
//% help=radio/on-received-string
//% blockId=radio_on_string_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedString(cb: (receivedString: string) => void) {
init();
onReceivedStringHandler = cb;
}
/**
* Registers code to run when the radio receives a buffer.
*/
//% help=radio/on-received-buffer blockHidden=1
//% blockId=radio_on_buffer_drag block="on radio received" blockGap=16
//% useLoc="radio.onDataPacketReceived" draggableParameters=reporter
export function onReceivedBuffer(cb: (receivedBuffer: Buffer) => void) {
init();
onReceivedBufferHandler = cb;
}
/**
* Returns properties of the last radio packet received.
* @param type the type of property to retrieve from the last packet
*/
//% help=radio/received-packet
//% weight=11 blockGap=8
//% blockId=radio_received_packet block="received packet %type=radio_packet_property" blockGap=16
export function receivedPacket(type: number) {
if (lastPacket) {
switch (type) {
case RadioPacketProperty.Time: return lastPacket.time;
case RadioPacketProperty.SerialNumber: return lastPacket.serial;
case RadioPacketProperty.SignalStrength: return lastPacket.signal;
}
}
return 0;
}
/**
* Gets a packet property.
* @param type the packet property type, eg: PacketProperty.time
*/
//% blockId=radio_packet_property block="%note"
//% shim=TD_ID blockHidden=1
export function _packetProperty(type: RadioPacketProperty): number {
return type;
}
export class RadioPacket {
public static getPacket(data: Buffer) {
// last 4 bytes is RSSi
return new RadioPacket(data);
}
public static mkPacket(packetType: number) {
const res = new RadioPacket();
res.data[0] = packetType;
return res;
}
private constructor(public readonly data?: Buffer) {
if (!data) this.data = control.createBuffer(DAL.MICROBIT_RADIO_MAX_PACKET_SIZE + 4);
}
get signal() {
return this.data.getNumber(NumberFormat.Int32LE, this.data.length - 4);
}
get packetType() {
return this.data[0];
}
get time() {
return this.data.getNumber(NumberFormat.Int32LE, 1);
}
set time(val: number) {
this.data.setNumber(NumberFormat.Int32LE, 1, val);
}
get serial() {
return this.data.getNumber(NumberFormat.Int32LE, 5);
}
set serial(val: number) {
this.data.setNumber(NumberFormat.Int32LE, 5, val);
}
get stringPayload() {
const offset = getStringOffset(this.packetType) as number;
return offset ? this.data.slice(offset + 1, this.data[offset]).toString() : undefined;
}
set stringPayload(val: string) {
const offset = getStringOffset(this.packetType) as number;
if (offset) {
const buf = control.createBufferFromUTF8(truncateString(val, getMaxStringLength(this.packetType)));
this.data[offset] = buf.length;
this.data.write(offset + 1, buf);
}
}
get numberPayload() {
switch (this.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_VALUE:
return this.data.getNumber(NumberFormat.Int32LE, PACKET_PREFIX_LENGTH);
case PACKET_TYPE_DOUBLE:
case PACKET_TYPE_DOUBLE_VALUE:
return this.data.getNumber(NumberFormat.Float64LE, PACKET_PREFIX_LENGTH);
}
return undefined;
}
set numberPayload(val: number) {
switch (this.packetType) {
case PACKET_TYPE_NUMBER:
case PACKET_TYPE_VALUE:
this.data.setNumber(NumberFormat.Int32LE, PACKET_PREFIX_LENGTH, val);
break;
case PACKET_TYPE_DOUBLE:
case PACKET_TYPE_DOUBLE_VALUE:
this.data.setNumber(NumberFormat.Float64LE, PACKET_PREFIX_LENGTH, val);
break;
}
}
get bufferPayload() {
const len = this.data[PACKET_PREFIX_LENGTH];
return this.data.slice(PACKET_PREFIX_LENGTH + 1, len);
}
set bufferPayload(b: Buffer) {
const len = Math.min(b.length, MAX_PAYLOAD_LENGTH - 1);
this.data[PACKET_PREFIX_LENGTH] = len;
this.data.write(PACKET_PREFIX_LENGTH + 1, b.slice(0, len));
}
hasString() {
return this.packetType === PACKET_TYPE_STRING ||
this.packetType === PACKET_TYPE_VALUE ||
this.packetType === PACKET_TYPE_DOUBLE_VALUE;
}
hasNumber() {
return this.packetType === PACKET_TYPE_NUMBER ||
this.packetType === PACKET_TYPE_DOUBLE ||
this.packetType === PACKET_TYPE_VALUE ||
this.packetType === PACKET_TYPE_DOUBLE_VALUE;
}
}
/**
* Broadcasts a number over radio to any connected micro:bit in the group.
*/
//% help=radio/send-number
//% weight=60
//% blockId=radio_datagram_send block="radio send number %value" blockGap=8
export function sendNumber(value: number) {
let packet: RadioPacket;
if (value === (value | 0)) {
packet = RadioPacket.mkPacket(PACKET_TYPE_NUMBER);
}
else {
packet = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE);
}
packet.numberPayload = value;
sendPacket(packet);
}
/**
* Broadcasts a name / value pair along with the device serial number
* and running time to any connected micro:bit in the group. The name can
* include no more than 8 characters.
* @param name the field name (max 8 characters), eg: "name"
* @param value the numeric value
*/
//% help=radio/send-value
//% weight=59
//% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8
export function sendValue(name: string, value: number) {
let packet: RadioPacket;
if (value === (value | 0)) {
packet = RadioPacket.mkPacket(PACKET_TYPE_VALUE);
}
else {
packet = RadioPacket.mkPacket(PACKET_TYPE_DOUBLE_VALUE);
}
packet.numberPayload = value;
packet.stringPayload = name;
sendPacket(packet);
}
/**
* Broadcasts a string along with the device serial number
* and running time to any connected micro:bit in the group.
*/
//% help=radio/send-string
//% weight=58
//% blockId=radio_datagram_send_string block="radio send string %msg"
//% msg.shadowOptions.toString=true
export function sendString(value: string) {
const packet = RadioPacket.mkPacket(PACKET_TYPE_STRING);
packet.stringPayload = value;
sendPacket(packet);
}
/**
* Broadcasts a buffer (up to 19 bytes long) along with the device serial number
* and running time to any connected micro:bit in the group.
*/
//% help=radio/send-buffer
//% weight=57
//% advanced=true
export function sendBuffer(msg: Buffer) {
const packet = RadioPacket.mkPacket(PACKET_TYPE_BUFFER);
packet.bufferPayload = msg;
sendPacket(packet);
}
/**
* Writes the last received packet to serial as JSON. This should be called
* within an ``onDataPacketReceived`` callback.
*/
//% help=radio/write-received-packet-to-serial
//% weight=3
//% blockId=radio_write_packet_serial block="radio write received packet to serial"
//% advanced=true
export function writeReceivedPacketToSerial() {
if (lastPacket) writeToSerial(lastPacket)
}
/**
* Set the radio to transmit the serial number in each message.
* @param transmit value indicating if the serial number is transmitted, eg: true
*/
//% help=radio/set-transmit-serial-number
//% weight=8 blockGap=8
//% blockId=radio_set_transmit_serial_number block="radio set transmit serial number %transmit"
//% advanced=true
export function setTransmitSerialNumber(transmit: boolean) {
transmittingSerial = transmit;
}
/**
* Gets the received signal strength indicator (RSSI) from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
*/
//% help=radio/received-signal-strength
//% weight=40
//% blockId=radio_datagram_rssi block="radio received signal strength"
//% deprecated=true blockHidden=true
export function receivedSignalStrength(): number {
return lastPacket ? lastPacket.signal : 0;
}
export function writeToSerial(packet: RadioPacket) {
serial.writeString("{");
serial.writeString("\"t\":");
serial.writeString("" + packet.time);
serial.writeString(",\"s\":");
serial.writeString("" + packet.serial);
if (packet.hasString()) {
serial.writeString(",\"n\":\"");
serial.writeString(packet.stringPayload);
serial.writeString("\"");
}
if (packet.packetType == PACKET_TYPE_BUFFER) {
serial.writeString(",\"b\":\"");
// TODO: proper base64 encoding
serial.writeString(packet.bufferPayload.toString());
serial.writeString("\"");
}
if (packet.hasNumber()) {
serial.writeString(",\"v\":");
serial.writeString("" + packet.numberPayload);
}
serial.writeString("}\r\n");
}
function sendPacket(packet: RadioPacket) {
packet.time = input.runningTime();
packet.serial = transmittingSerial ? control.deviceSerialNumber() : 0;
radio.sendRawPacket(packet.data);
}
function truncateString(str: string, bytes: number) {
str = str.substr(0, bytes);
let buff = control.createBufferFromUTF8(str);
while (buff.length > bytes) {
str = str.substr(0, str.length - 1);
buff = control.createBufferFromUTF8(str);
}
return str;
}
function getStringOffset(packetType: number) {
switch (packetType) {
case PACKET_TYPE_STRING:
return PACKET_PREFIX_LENGTH;
case PACKET_TYPE_VALUE:
return VALUE_PACKET_NAME_LEN_OFFSET;
case PACKET_TYPE_DOUBLE_VALUE:
return DOUBLE_VALUE_PACKET_NAME_LEN_OFFSET;
default:
return undefined;
}
}
function getMaxStringLength(packetType: number) {
switch (packetType) {
case PACKET_TYPE_STRING:
return MAX_PAYLOAD_LENGTH - 2;
case PACKET_TYPE_VALUE:
case PACKET_TYPE_DOUBLE_VALUE:
return MAX_FIELD_DOUBLE_NAME_LENGTH;
default:
return undefined;
}
}
}

144
libs/radio/shims.d.ts vendored
View File

@ -2,66 +2,30 @@
//% color=270 weight=34
//% color=#E3008C weight=96 icon="\uf012"
declare namespace radio {
/**
* Broadcasts a number over radio to any connected micro:bit in the group.
* Sends an event over radio to neigboring devices
*/
//% help=radio/send-number
//% weight=60
//% blockId=radio_datagram_send block="radio send number %value" blockGap=8 shim=radio::sendNumber
function sendNumber(value: number): void;
//% blockId=radioRaiseEvent block="radio raise event|from source %src=control_event_source_id|with value %value=control_event_value_id"
//% blockExternalInputs=1
//% advanced=true
//% weight=1
//% help=radio/raise-event shim=radio::raiseEvent
function raiseEvent(src: int32, value: int32): void;
/**
* Broadcasts a name / value pair along with the device serial number
* and running time to any connected micro:bit in the group.
* @param name the field name (max 12 characters), eg: "name"
* @param value the numberic value
* Internal use only. Takes the next packet from the radio queue and returns its contents + RSSI in a Buffer
*/
//% help=radio/send-value
//% weight=59
//% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8 shim=radio::sendValue
function sendValue(name: string, value: number): void;
//% shim=radio::readRawPacket
function readRawPacket(): Buffer;
/**
* Broadcasts a string along with the device serial number
* and running time to any connected micro:bit in the group.
* Internal use only. Sends a raw packet through the radio (assumes RSSI appened to packet)
*/
//% help=radio/send-string
//% weight=58
//% blockId=radio_datagram_send_string block="radio send string %msg" shim=radio::sendString
function sendString(msg: string): void;
/**
* Reads the next packet from the radio queue and and writes it to serial
* as JSON.
*/
//% help=radio/write-value-to-serial
//% weight=3
//% blockId=radio_write_value_serial block="radio write value to serial"
//% deprecated=true shim=radio::writeValueToSerial
function writeValueToSerial(): void;
/**
* Writes the last received packet to serial as JSON. This should be called
* within an ``onDataPacketReceived`` callback.
*/
//% help=radio/write-received-packet-to-serial
//% weight=3
//% blockId=radio_write_packet_serial block="radio write received packet to serial"
//% advanced=true shim=radio::writeReceivedPacketToSerial
function writeReceivedPacketToSerial(): void;
/**
* Reads the next packet from the radio queue and returns the packet's number
* payload or 0 if the packet did not contain a number.
*/
//% help=radio/receive-number
//% weight=46
//% blockId=radio_datagram_receive block="radio receive number" blockGap=8
//% deprecated=true shim=radio::receiveNumber
function receiveNumber(): number;
//% async shim=radio::sendRawPacket
function sendRawPacket(msg: Buffer): void;
/**
* Registers code to run when a packet is received over radio.
@ -72,35 +36,15 @@ declare namespace radio {
//% deprecated=true shim=radio::onDataReceived
function onDataReceived(body: () => void): void;
/**
* Reads the next packet from the radio queue and returns the packet's string
* payload or the empty string if the packet did not contain a string.
*/
//% blockId=radio_datagram_receive_string block="radio receive string" blockGap=8
//% weight=44
//% help=radio/receive-string
//% deprecated=true shim=radio::receiveString
function receiveString(): string;
/**
* Gets the received signal strength indicator (RSSI) from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc). Not supported in simulator.
* namespace=radio
*/
//% help=radio/received-signal-strength
//% weight=40
//% blockId=radio_datagram_rssi block="radio received signal strength"
//% deprecated=true shim=radio::receivedSignalStrength
function receivedSignalStrength(): number;
/**
* Sets the group id for radio communications. A micro:bit can only listen to one group ID at any time.
* @ param id the group id between ``0`` and ``255``, 1 eg
* @param id the group id between ``0`` and ``255``, eg: 1
*/
//% help=radio/set-group
//% weight=10 blockGap=8 advanced=true
//% blockId=radio_set_group block="radio set group %ID" shim=radio::setGroup
function setGroup(id: number): void;
//% weight=100
//% blockId=radio_set_group block="radio set group %ID"
//% id.min=0 id.max=255 shim=radio::setGroup
function setGroup(id: int32): void;
/**
* Change the output power level of the transmitter to the given value.
@ -109,50 +53,20 @@ declare namespace radio {
//% help=radio/set-transmit-power
//% weight=9 blockGap=8
//% blockId=radio_set_transmit_power block="radio set transmit power %power"
//% power.min=0 power.max=7
//% advanced=true shim=radio::setTransmitPower
function setTransmitPower(power: number): void;
function setTransmitPower(power: int32): void;
/**
* Set the radio to transmit the serial number in each message.
* @param transmit value indicating if the serial number is transmitted, eg: true
*/
//% help=radio/set-transmit-serial-number
* Change the transmission and reception band of the radio to the given channel
* @param band a frequency band in the range 0 - 83. Each step is 1MHz wide, based at 2400MHz.
**/
//% help=radio/set-frequency-band
//% weight=8 blockGap=8
//% blockId=radio_set_transmit_serial_number block="radio set transmit serial number %transmit"
//% advanced=true shim=radio::setTransmitSerialNumber
function setTransmitSerialNumber(transmit: boolean): void;
/**
* Returns the number payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or 0 if that packet did not
* contain a number.
*/
//% help=radio/received-number shim=radio::receivedNumber
function receivedNumber(): number;
/**
* Returns the serial number of the sender micro:bit from the last packet taken
* from the radio queue (via ``receiveNumber``, ``receiveString``, etc) or 0 if
* that packet did not send a serial number.
*/
//% help=radio/received-serial shim=radio::receivedSerial
function receivedSerial(): number;
/**
* Returns the string payload from the last packet taken from the radio queue
* (via ``receiveNumber``, ``receiveString``, etc) or the empty string if that
* packet did not contain a string.
*/
//% help=radio/received-string shim=radio::receivedString
function receivedString(): string;
/**
* Returns the system time of the sender micro:bit at the moment when it sent the
* last packet taken from the radio queue (via ``receiveNumber``,
* ``receiveString``, etc).
*/
//% help=radio/received-time shim=radio::receivedTime
function receivedTime(): number;
//% blockId=radio_set_frequency_band block="radio set frequency band %band"
//% band.min=0 band.max=83
//% advanced=true shim=radio::setFrequencyBand
function setFrequencyBand(band: int32): void;
}
// Auto-generated. Do not edit. Really.

349
libs/radio/test.ts Normal file
View File

@ -0,0 +1,349 @@
/**
* Tests for the radio. Press A on mbit 1 and B on mbit 2 to run the tests.
* Sends random ints, doubles, strings, and buffers and checks them on
* the other side
*/
class FastRandom {
private lfsr: number;
public seed: number;
constructor(seed?: number) {
if (seed === undefined) seed = Math.randomRange(0x0001, 0xFFFF);
this.seed = seed;
this.lfsr = seed;
}
next(): number {
return this.lfsr = (this.lfsr >> 1) ^ ((-(this.lfsr & 1)) & 0xb400);
}
randomRange(min: number, max: number): number {
return min + (max > min ? this.next() % (max - min + 1) : 0);
}
reset() {
this.lfsr = this.seed;
}
}
enum TestStage {
Integer,
String,
Double,
IntValue,
DblValue,
Buffer
}
const TEST_COUNT = 30;
radio.setGroup(78)
const rand = new FastRandom(1234);
let stage = TestStage.Integer;
function initSender() {
let lastReceived: number;
let lastString: string;
let testIndex = 0;
let lastBuf: Buffer;
let lastAck = -1;
rand.reset();
basic.clearScreen();
// Send loop
control.inBackground(function () {
while (true) {
for (let i = 0; i < TEST_COUNT; i++) {
toggle(testIndex);
if (stage === TestStage.Integer) {
lastReceived = getNextInt();
}
else if (stage === TestStage.Double) {
lastReceived = getNextDouble();
}
else if (stage === TestStage.IntValue) {
lastString = getNextName();
console.log(truncateString(lastString, 8))
lastReceived = getNextInt();
}
else if (stage === TestStage.DblValue) {
lastString = getNextName();
lastReceived = getNextDouble();
}
else if (stage === TestStage.String) {
lastString = getNextString();
console.log(truncateString(lastString, 19))
}
else if (stage === TestStage.Buffer) {
lastBuf = getNextBuffer();
}
while (lastAck !== testIndex) {
if (stage === TestStage.Integer || stage === TestStage.Double) {
radio.sendNumber(lastReceived)
}
else if (stage === TestStage.IntValue || stage === TestStage.DblValue) {
radio.sendValue(lastString, lastReceived)
}
else if (stage === TestStage.String) {
radio.sendString(lastString);
}
else if (stage === TestStage.Buffer) {
radio.sendBuffer(lastBuf);
}
basic.pause(10);
}
testIndex++;
}
stage++;
if (stage > TestStage.Buffer) {
basic.showIcon(IconNames.Yes);
return;
}
}
})
radio.onReceivedNumber(function (receivedNumber: number) {
if (receivedNumber > lastAck) {
lastAck = receivedNumber;
}
});
}
let lastReceived: number;
let lastString: string;
let testIndex = -1;
let running = true;
let lastBuf: Buffer;
let lastPacket = new radio.Packet();
let currentPacket = new radio.Packet();
function truncateString(str: string, bytes: number) {
str = str.substr(0, bytes);
let buff = control.createBufferFromUTF8(str);
while (buff.length > bytes) {
str = str.substr(0, str.length - 1);
buff = control.createBufferFromUTF8(str);
}
return str;
}
function initReceiver() {
rand.reset();
basic.clearScreen();
radio.onDataReceived(function () {
radio.receiveNumber();
currentPacket.receivedNumber = radio.receivedNumber();
currentPacket.receivedString = radio.receivedString();
currentPacket.receivedBuffer = radio.receivedBuffer();
if (currentPacket.receivedNumber === lastPacket.receivedNumber &&
currentPacket.receivedString === lastPacket.receivedString &&
checkBufferEqual(currentPacket.receivedBuffer, lastPacket.receivedBuffer)) {
return;
}
lastPacket.receivedNumber = currentPacket.receivedNumber
lastPacket.receivedString = currentPacket.receivedString
lastPacket.receivedBuffer = currentPacket.receivedBuffer
switch (stage) {
case TestStage.Integer:
verifyInt(radio.receivedNumber());
break;
case TestStage.Double:
verifyDouble(radio.receivedNumber());
break;
case TestStage.IntValue:
verifyIntValue(radio.receivedString(), radio.receivedNumber());
break;
case TestStage.DblValue:
verifyDblValue(radio.receivedString(), radio.receivedNumber());
break;
case TestStage.String:
verifyString(radio.receivedString());
break;
case TestStage.Buffer:
verifyBuffer(radio.receivedBuffer());
break;
}
})
control.inBackground(function () {
while (running) {
radio.sendNumber(testIndex);
basic.pause(10)
}
})
}
function nextTest() {
testIndex++;
toggle(testIndex);
console.log(`test ${testIndex}`)
if (((testIndex + 1) % TEST_COUNT) === 0) {
stage++;
if (stage > TestStage.Buffer) {
basic.showIcon(IconNames.Yes)
running = false;
}
}
}
function verifyInt(int: number) {
if (int === lastReceived) return;
lastReceived = int;
if (lastReceived != getNextInt()) fail();
nextTest()
}
function verifyDouble(dbl: number) {
if (dbl === lastReceived) return;
lastReceived = dbl;
if (lastReceived != getNextDouble()) fail();
nextTest()
}
function verifyIntValue(name: string, val: number) {
if (val === lastReceived) return;
lastReceived = val;
if (name != truncateString(getNextName(), 8) || lastReceived != getNextInt()) fail();
nextTest()
}
function verifyDblValue(name: string, val: number) {
if (val === lastReceived) return;
lastReceived = val;
if (name != truncateString(getNextName(), 8) || lastReceived != getNextDouble()) fail();
nextTest()
}
function verifyString(str: string) {
if (!str || str === lastString) return;
lastString = str;
let next = truncateString(getNextString(), 19);
if (lastString !== next) {
console.log(`got ${control.createBufferFromUTF8(lastString).toHex()} expected ${control.createBufferFromUTF8(next).toHex()}`)
}
nextTest()
}
function verifyBuffer(buf: Buffer) {
if (checkBufferEqual(lastBuf, buf)) return;
lastBuf = buf;
if (!checkBufferEqual(lastBuf, getNextBuffer())) {
fail();
}
nextTest()
}
function fail() {
control.panic(testIndex);
}
let _lastInt: number;
let _lastDbl: number;
let _lastStr: string;
let _lastBuf: Buffer;
let _lastNam: string;
function getNextInt(): number {
let res = rand.next();
if (!res || res === _lastInt) return getNextInt();
_lastInt = res;
return res;
}
function getNextDouble(): number {
let res = rand.next() / rand.next();
if (res === _lastDbl) return getNextDouble();
_lastDbl = res;
return res;
}
function getNextString(): string {
let len = rand.randomRange(1, 19);
let res = "";
for (let i = 0; i < len; i++) {
res += String.fromCharCode(rand.next() & 0xbfff);
}
if (res === _lastStr) return getNextString();
_lastStr = res;
return res;
}
function getNextName(): string {
let len = rand.randomRange(1, 8);
let res = "";
for (let i = 0; i < len; i++) {
res += String.fromCharCode(rand.next() & 0xbfff);
}
if (res === _lastNam) return getNextName();
_lastNam = res;
return res;
}
function getNextBuffer(): Buffer {
let len = rand.randomRange(0, 8);
let res = control.createBuffer(len);
for (let i = 0; i < len; i++) {
res[i] = rand.next() & 0xff;
}
if (checkBufferEqual(_lastBuf, res)) return getNextBuffer();
_lastBuf = res;
return res;
}
function checkBufferEqual(a: Buffer, b: Buffer) {
if (a === b) return true;
if ((!a && b) || (a && !b)) return false;
if (a.length != b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
input.onButtonPressed(Button.A, function () {
basic.showString("S");
initSender();
})
input.onButtonPressed(Button.B, function () {
basic.showString("R");
initReceiver();
})
function toggle(index: number) {
const x = index % 5;
const y = Math.idiv(index, 5) % 5;
led.toggle(x, y);
}