diff --git a/.yotta_ignore b/.yotta_ignore new file mode 100644 index 0000000..c8da81a --- /dev/null +++ b/.yotta_ignore @@ -0,0 +1,2 @@ +cmake-build-debug/* +CMakeLists.txt diff --git a/README-STACK.md b/README-STACK.md new file mode 100644 index 0000000..caebe80 --- /dev/null +++ b/README-STACK.md @@ -0,0 +1,140 @@ +#How to configure the Stack and heap + +The Stack and the heap are configured in 2 different ways. + +## 1: Standard memory configuration +The basic file is [startup_NRF51822.S](yotta_modules/mbed-classic/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/TOOLCHAIN_GCC_ARM/startup_NRF51822.S), +which should not be modified, if you do not exactly know what to do. + +However in this file there are two defines, `__STACK_SIZE` and `__HEAP_SIZE` +which can be used to modify the stack and the heap from outside this function. + +### 1.1: Modifying toolchain.cmake +One option to do so, is to extend the file [toolchain.cmake](yotta_targets/calliope-mini-classic-gcc/CMake/toolchain.cmake). Add `-D__STACK_SIZE=2632 -D__HEAP_SIZE=1464` at the end of `add_definitions("...` +and the stack will have the size of 2632 Bytes and the heap will have the size of 1464 Bytes. + +### 1.2: Adding defines.json +The alternative option is to add a [defines.json](defines.json) file to the same directory, where [module.json](module.json) is located. +Therein macro definitions can be added for the application. In this case the file would look like this: +```[bash] +{ + "__STACK_SIZE": 2632, + "__HEAP_SIZE": 1464 +} +``` +The advantage of this option is, that you do not have to modify any dependencies, but can add extra definitions to the application. +The disadvantage is, that these definitions will apply to all application code, which can lead multiple definitions. + +**NOTE: The size of Stack + Heap has to fit into the RAM** + +#### 1.2.1 How much space for Heap and Stack is in the RAM + +To find out how much space is in the RAM, you have to read the [calliope-demo.elf](build/calliope-mini-classic-gcc/source/calliope-demo) +file. Therefore enter the following command in the terminal: +```[bash] +$ readelf -S calliope-demo +``` +The output will look like this: +```[bash] +There are 22 section headers, starting at offset 0x326b10: + +Section Headers: + [Nr] Name Type Addr Off Size ES Flg Lk Inf Al + [ 0] NULL 00000000 000000 000000 00 0 0 0 + [ 1] .text PROGBITS 00018000 008000 016494 00 AX 0 0 8 + [ 2] .ARM.exidx ARM_EXIDX 0002e494 01e494 000008 00 AL 1 0 4 + [ 3] .data PROGBITS 20002000 022000 0000fc 00 WA 0 0 4 + [ 4] .bss NOBITS 20002100 022100 000a20 00 WA 0 0 8 + [ 5] .heap PROGBITS 20002b20 022100 000acc 00 0 0 8 + [ 6] .stack_dummy PROGBITS 20002b20 022bd0 000a00 00 0 0 8 + [ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 0235d0 000028 00 0 0 1 + [ 8] .comment PROGBITS 00000000 0235f8 00007e 01 MS 0 0 1 + [ 9] .debug_info PROGBITS 00000000 023676 220d76 00 0 0 1 + [10] .debug_abbrev PROGBITS 00000000 2443ec 028b2e 00 0 0 1 + [11] .debug_loc PROGBITS 00000000 26cf1a 02800e 00 0 0 1 + [12] .debug_aranges PROGBITS 00000000 294f28 003268 00 0 0 8 + [13] .debug_ranges PROGBITS 00000000 298190 006958 00 0 0 1 + [14] .debug_line PROGBITS 00000000 29eae8 035e48 00 0 0 1 + [15] .debug_str PROGBITS 00000000 2d4930 02f121 01 MS 0 0 1 + [16] .debug_frame PROGBITS 00000000 303a54 009790 00 0 0 4 + [17] .stab PROGBITS 00000000 30d1e4 00003c 0c 18 0 4 + [18] .stabstr STRTAB 00000000 30d220 000076 00 0 0 1 + [19] .symtab SYMTAB 00000000 30d298 00dac0 10 20 2352 4 + [20] .strtab STRTAB 00000000 31ad58 00bcde 00 0 0 1 + [21] .shstrtab STRTAB 00000000 326a36 0000d8 00 0 0 1 +Key to Flags: + W (write), A (alloc), X (execute), M (merge), S (strings) + I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) + O (extra OS processing required) o (OS specific), p (processor specific) +``` +The important number here is at `[ 5] .heap PROGBITS 20002b20`, +which is the hexadecimal address = **0x2002b20** where the **Heap starts**. Since the **Stack ends** at the address **0x20004000**, +the space in beetween can be used for Heap and Stack and defined in [defines.json](defines.json) and [config.json](config.json) + +**Important for config.json:** +The following settings have to be set: + + +```[bash] +{ + ... + "debug": 0, + "heap_debug": 0, + "reuse_sd": 0, + "heap_allocator": 0, + "stack_size": 2560, + } +} +``` +where `stack_size` has to be the same as `__STACK_SIZE` + + + + +# !!!DEPRECATED!!! + +## 2: MicroBit custom heap configuration +Since the [microbit-dal](yotta_modules/microbit-dal) +has a custom heap allocator [MicroBitHeapAllocator.cpp](yotta_modules/microbit-dal/source/core/MicroBitHeapAllocator.cpp) +and [MicroBitHeapAllocator.h](yotta_modules/microbit-dal/inc/core/MicroBitHeapAllocator.h), +which is configured from [config.json](config.json), this file has to be modified to fit the standard configuration, +by adding + +```[bash] +"microbit-dal": { + ... + "nested_heap_proportion": 0.6, + "stack_size": 2632, + ... + } +``` + +### 2.1: Calculation of the custom heap configuration +(this chapter is based on my findings and has to be approved by the people who made coded the microbit-dal) + +Currently the custom heap assumes that Heap + Stack = 4672 Bytes. +Unfortunatelly this seems to be not the case. + +Running the [memsum.py](contrib/memsum.py) script with debug and hepa-debug enabled, +it shows that the custom heap starts from `0x200030F0` and from [MicroBitConfig.h](yotta_modules/microbit-dal/inc/core/MicroBitConfig.h) +we know that the Stack ends at `0x20004000` which is the upper bound of the RAM. + +This means the real space for Heap + Stack is 3856 Bytes. + +During testing, I found out, that whenever the Heap goes out of bounds, the controller crashes. +Also the overlapping of the stack and the Heap region brings the controller to crash sometimes. + +The calculation follows: + +`heap_size = (4672 - stack_size) * nested_heap_proportion` + +However with the given knowledge the calculation should be: +`nested_heap_proportion' = (stack_size - 3856) / (stack_size - 4672)` +in which case the stack and the nested heap will not overlap. + + +## Further remarks +The option `"reuse_sd": 0` has to be 0 if BLE is used, otherwise heap will be allocated in the Softdevice RAM region, +for BLE nad thus this region will be overriden by the Softdevice and caus the controller to crash. + + diff --git a/README-UUIDS.md b/README-UUIDS.md new file mode 100644 index 0000000..3582d8d --- /dev/null +++ b/README-UUIDS.md @@ -0,0 +1,42 @@ +[BluetoothServiceNotify](source/BluetoothServiceNotify.cpp) + +BluetoothServiceNotifyUUID = 0xff,0x55,0xdd,0xee, 0x25,0x1d, 0x47,0x0a, 0xa0,0x62, 0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +[BluetoothServiceProgram](source/BluetoothServiceProgram.h) + +BluetoothServiceProgramUUID = 0xff, 0x66, 0xdd, 0xee, 0x25, 0x1d, 0x47, 0x0a, 0xa0, 0x62, 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 + +[MicroBitAccelerometerService](yotta_modules/microbit-dal/source/bluetooth/MicroBitAccelerometerService.cpp) + +MicroBitAccelerometerServiceUUID = 0xe9,0x5d,0x07,0x53,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +MicroBitAccelerometerServiceDataUUID = 0xe9,0x5d,0xca,0x4b,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +MicroBitAccelerometerServicePeriodUUID = 0xe9,0x5d,0xfb,0x24,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +[MicroBitDFUService](yotta_modules/microbit-dal/source/bluetooth/MicroBitDFUService.cpp) + +MicroBitDFUServiceUUID = 0xe9,0x5d,0x93,0xb0,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +MicroBitDFUServiceControlCharacteristicUUID = 0xe9,0x5d,0x93,0xb1,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 + +[DeviceInfoService](yotta_modules/ble/ble/GattCharacteristic.h) + +UUID_DEVICE_INFORMATION_SERVICE = 0x180A + +UUID_MANUFACTURER_NAME_STRING_CHAR = 0x2A29 +UUID_MODEL_NUMBER_STRING_CHAR = 0x2A24 +UUID_SERIAL_NUMBER_STRING_CHAR = 0x2A25 +UUID_HARDWARE_REVISION_STRING_CHAR = 0x2A27 +UUID_FIRMWARE_REVISION_STRING_CHAR = 0x2A26 +UUID_SOFTWARE_REVISION_STRING_CHAR = 0x2A28 + + + +**Currently not in Use** + + +[BluetoothServiceDebug](source/BluetoothServiceDebug.cpp) + +BluetoothServiceDebugUUID = 0xff, 0x44, 0xdd, 0xee, 0x25, 0x1d, 0x47, 0x0a, 0xa0, 0x62, 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 + diff --git a/config.json b/config.json index 6eb31ec..c8012e2 100644 --- a/config.json +++ b/config.json @@ -4,16 +4,35 @@ "enabled": 1, "pairing_mode": 1, "private_addressing": 0, - "open": 0, - "whitelist": 1, + "open": 1, + "security_level": "SECURITY_MODE_ENCRYPTION_OPEN_LINK", + "whitelist": 0, "advertising_timeout": 0, - "tx_power": 6, + "tx_power": 7, "dfu_service": 1, "event_service": 0, - "device_info_service": 1, - "security_level": "SECURITY_MODE_ENCRYPTION_NO_MITM" + "device_info_service": 1 }, - "gatt_table_size": "0x700", - "debug": 0 + "gatt_table_size": "0x600", + "debug": 0, + "heap_debug": 0, + "reuse_sd": 0, + "default_pullmode": "PullDown", + "heap_allocator": 0, + "nested_heap_proportion": 0.6, + "system_tick_period": 6, + "system_components": 10, + "idle_components": 6, + "use_accel_lsb": 0, + "min_display_brightness": 1, + "max_display_brightness": 255, + "display_scroll_speed": 120, + "display_scroll_stride": -1, + "display_print_speed": 400, + "panic_on_heap_full": 0, + "stack_size": 2560, + "sram_base": "0x20000008", + "sram_end": "0x20004000", + "sd_limit": "0x20002000" } } diff --git a/contrib/copy.sh b/contrib/copy.sh new file mode 100755 index 0000000..c83ba88 --- /dev/null +++ b/contrib/copy.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +SRC=../../calliope_playbook_mini/source/ +DST=`pwd`/source/ + +rsync -rtuv --progress $SRC $DST \ + --exclude Main.* diff --git a/contrib/log.sh b/contrib/log.sh new file mode 100755 index 0000000..db155fb --- /dev/null +++ b/contrib/log.sh @@ -0,0 +1,8 @@ + +#!/bin/sh + +ROOT="$( cd "$(dirname "$0")" ; pwd -P )" +DEVICE=/dev/cu.usbmodem1422 + +( stty speed 115200 cs8 1>/dev/null 2>&1; cat ) <$DEVICE +#( stty speed 115200 cs8 1>/dev/null 2>&1; hexdump -C ) <$DEVICE diff --git a/contrib/memsum.py b/contrib/memsum.py new file mode 100644 index 0000000..bf85dff --- /dev/null +++ b/contrib/memsum.py @@ -0,0 +1,65 @@ +import re +import sys +import time + + +mem = {} +allocated = 0 + +reset = None +if len(sys.argv) > 1: + reset = sys.argv[1] + print("RESETTING tracer on '%s'" % reset) + +r_malloc = re.compile("^(microbit_)malloc:\\s+(NATIVE\\s+)?(ALLOCATED:)\\s+(\\d+)\\s+\\[(0x[0-9a-f]+)\\]") +r_free = re.compile("^(microbit_)free:\\s+(0x[0-9a-f]+)") + +partial = "" +#broken in python2 +#for line in sys.stdin: +for line in iter(sys.stdin.readline, ''): + # we sometimes get incomplete lines, wait for a full line + if not (line[-1] == '\n' or line[-1] == '\r'): + partial = line + continue + else: + line = partial + line + partial = "" + + # strip newline and carriage return + line = line.rstrip('\n').rstrip('\r') + + # if we detect the reset keyword, rest the map and memory counter + if reset != None and reset in line: + mem = {} + allocated = 0 + # print("\n\n\033[91m>> RESET >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m") + print("\033[H\033[J") + + # match malloc, realloc and free + m = r_malloc.search(line) + if m: + mem[m.group(5)] = int(m.group(4)) + allocated += int(m.group(4)) + print("\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[31m+%-6d\033[0m (%s)" % \ + (len(mem), allocated, allocated, int(m.group(4)), + m.group(0).replace(m.group(1), "").replace(m.group(3), ""))) + continue + + m = r_free.search(line) + if m: + # print "f", m.group(3) + freed = 0 + if mem.has_key(m.group(2)): + freed = mem[m.group(2)] + allocated -= freed + del mem[m.group(2)] + else: + print ("\033[33m!! WARN: %s\033[0m" % (line)) + print ("\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[92m-%-6d\033[0m (%s)" % \ + (len(mem), allocated, allocated, freed, m.group(0).replace(m.group(1), ""))) + continue + + # print all other lines as is, so we can still use the log functionality + print(line) + sys.stdout.flush() diff --git a/contrib/memsum.sh b/contrib/memsum.sh new file mode 100755 index 0000000..73a470d --- /dev/null +++ b/contrib/memsum.sh @@ -0,0 +1,7 @@ + +#!/bin/sh + +ROOT="$( cd "$(dirname "$0")" ; pwd -P )" +DEVICE=/dev/ttyACM0 + +( stty speed 115200 cs8 1>/dev/null 2>&1; python2 $ROOT/memsum.py Calliope) <$DEVICE diff --git a/contrib/openocd.cfg b/contrib/openocd.cfg new file mode 100644 index 0000000..3a90556 --- /dev/null +++ b/contrib/openocd.cfg @@ -0,0 +1,10 @@ +source [find interface/cmsis-dap.cfg] +source [find target/nrf51.cfg] + +$_TARGETNAME configure -event gdb-attach { + reset init +} + +$_TARGETNAME configure -event gdb-flash-write-end { + reset init +} \ No newline at end of file diff --git a/contrib/upload.sh b/contrib/upload.sh new file mode 100755 index 0000000..6380108 --- /dev/null +++ b/contrib/upload.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +cp build/calliope-mini-classic-gcc/source/*-combined.hex /Volumes/MINI/ + +if [ -x $HOME/Shared/Manolis ]; then + cp build/calliope-mini-classic-gcc/source/*-combined.hex $HOME/Shared/Manolis +fi diff --git a/contrib/yotta.sh b/contrib/yotta.sh new file mode 100755 index 0000000..51138eb --- /dev/null +++ b/contrib/yotta.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker run -v $(pwd):/project:rw -it calliopeedu/yotta yotta $1 diff --git a/defines.json b/defines.json new file mode 100644 index 0000000..e3fd193 --- /dev/null +++ b/defines.json @@ -0,0 +1,4 @@ +{ + "__STACK_SIZE": 2560, + "__HEAP_SIZE": 2764 +} diff --git a/module.json b/module.json index 291eb96..bc086c4 100644 --- a/module.json +++ b/module.json @@ -4,8 +4,8 @@ "description": "Calliope mini Demo", "license": "Apache 2.0", "dependencies": { - "microbit": "calliope-mini/microbit#v2.0.0-rc8-calliope-1.0.2" + "microbit": "calliope-mini/microbit#v2.0.0-calliope-1.0.5" }, "targetDependencies": {}, "bin": "./source" -} +} \ No newline at end of file diff --git a/source/BluetoothServiceDebug.cpp b/source/BluetoothServiceDebug.cpp new file mode 100644 index 0000000..a254b1a --- /dev/null +++ b/source/BluetoothServiceDebug.cpp @@ -0,0 +1,114 @@ +#include "BluetoothServiceDebug.h" +#include "MicroBit.h" +#include "Bytes.h" + +extern MicroBit uBit; + +static const uint8_t BluetoothServiceDebugUUID[] = { + 0xff, 0x44, 0xdd, 0xee, + 0x25, 0x1d, + 0x47, 0x0a, + 0xa0, 0x62, + 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 +}; + +BluetoothServiceDebug::BluetoothServiceDebug(Interpreter& _interpreter) + : interpreter(_interpreter) + , ble(*uBit.ble) + , characteristic( + BluetoothServiceDebugUUID, + (uint8_t*)&characteristicsBuffer, 0, sizeof(characteristicsBuffer), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ) + , characteristicsBuffer() + , selector() + , address() +{ + characteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL); + characteristic.setReadAuthorizationCallback(this, &BluetoothServiceDebug::onDataRead); + + GattCharacteristic* characteristics[] = { + &characteristic + }; + + GattService service( + BluetoothServiceDebugUUID, + characteristics, + sizeof(characteristics) / sizeof(GattCharacteristic*)); + + ble.addService(service); + + characteristicsHandle = characteristic.getValueHandle(); + + ble.onDataWritten(this, &BluetoothServiceDebug::onDataWritten); +} + +void BluetoothServiceDebug::onDataWritten(const GattWriteCallbackParams* params) +{ + if (params->handle == characteristicsHandle) { + const uint8_t* data = params->data; + const uint16_t len = params->len; + + // set selector, hi(address), lo(address) + if (len == 3) { + selector = data[0]; + address = BYTES_TO_UINT16(data[1], data[2]); + } + } +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +void BluetoothServiceDebug::onDataRead(GattReadAuthCallbackParams* params) +{ + if (params->handle == characteristicsHandle) { + + // get code at address + if (selector == 0x00) { + + const uint8_t* buffer = &interpreter.code[0]; + + const uint16_t start = static_cast MIN(address, CODE_LEN); + const uint16_t stop = static_cast MIN(address + 16, CODE_LEN); + const uint16_t len = stop - start; + + uint8_t data[1 + 2 + 16]; + data[0] = 0x00; + data[1] = HI16(address); + data[2] = LO16(address); + + if (len > 0) { + memcpy(&data[3], buffer + start, len); + } + + ble.gattServer().write( + characteristicsHandle, + data, + static_cast(3 + len)); + + } else + + // get methods at address + if (selector == 0x01) { + + const uint8_t* buffer = (uint8_t*)&interpreter.methods[0]; + + const uint16_t start = static_cast MIN(address, METHODS_COUNT * 2); + const uint16_t stop = static_cast MIN(address + 2, METHODS_COUNT * 2); + const uint16_t len = stop - start; + + uint8_t data[1 + 2 + 2]; + data[0] = 0x01; + data[1] = HI16(address); + data[2] = LO16(address); + + if (len > 0) { + memcpy(&data[3], buffer + start, len); + } + + ble.gattServer().write( + characteristicsHandle, + data, + static_cast(3 + len)); + } + } +} \ No newline at end of file diff --git a/source/BluetoothServiceDebug.h b/source/BluetoothServiceDebug.h new file mode 100644 index 0000000..7234e5e --- /dev/null +++ b/source/BluetoothServiceDebug.h @@ -0,0 +1,42 @@ +#ifndef BLUETOOTH_SERVICE_DEBUG_H +#define BLUETOOTH_SERVICE_DEBUG_H + +#include "Interpreter.h" +#include "ble/BLE.h" + +/*! + * @class BluetoothServiceDebug + * + */ +class BluetoothServiceDebug { +public: + /*! + * Constructor. + * Create a representation of BluetoothServiceDebug + * @param interpreter Reference to an Interpreter + */ + BluetoothServiceDebug(Interpreter& interpreter); + + /*! + * Callback. Invoked when any of our attributes are written via BLE. + */ + void onDataWritten(const GattWriteCallbackParams* params); + + /*! + * Callback. Invoked when any of our attributes are read via BLE. + */ + void onDataRead(GattReadAuthCallbackParams* params); + +private: + Interpreter& interpreter; + BLEDevice& ble; + + GattAttribute::Handle_t characteristicsHandle; + GattCharacteristic characteristic; + uint8_t characteristicsBuffer[CHARACTERISTICS_BUFFER_LEN]; + + uint8_t selector; + uint16_t address; +}; + +#endif //BLUETOOTH_SERVICE_DEBUG_H \ No newline at end of file diff --git a/source/BluetoothServiceNotify.cpp b/source/BluetoothServiceNotify.cpp new file mode 100644 index 0000000..7455dd2 --- /dev/null +++ b/source/BluetoothServiceNotify.cpp @@ -0,0 +1,93 @@ +#include "BluetoothServiceNotify.h" +#include "MicroBit.h" +#include "Bytes.h" +#include "BluetoothServiceProgram.h" + + +extern MicroBit uBit; + +static const uint8_t BluetoothServiceNotifyUUID[] = { + 0xff,0x55,0xdd,0xee, + 0x25,0x1d, + 0x47,0x0a, + 0xa0,0x62, + 0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +BluetoothServiceNotify::BluetoothServiceNotify(Interpreter &_interpreter) : + interpreter(_interpreter), + ble(*uBit.ble), + characteristic( + BluetoothServiceNotifyUUID, + (uint8_t *)&characteristicsBuffer, 0, sizeof(characteristicsBuffer), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY + ), + characteristicsBuffer() +{ + characteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK); + characteristic.setReadAuthorizationCallback(this, &BluetoothServiceNotify::onDataRead); + + GattCharacteristic *characteristics[] = { + &characteristic + }; + + GattService service( + BluetoothServiceNotifyUUID, + characteristics, + sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + + // TODO make this configuration dependent +#ifdef TARGET_NRF51_CALLIOPE + ManagedString namePrefix("Calliope mini ["); +#else + ManagedString namePrefix("BBC micro:bit ["); +#endif + ManagedString namePostfix("]"); + ManagedString BLEName = namePrefix + microbit_friendly_name() + namePostfix; + + + // Update the advertised name of this micro:bit to include the device name + ble.clearAdvertisingPayload(); + + ble.accumulateAdvertisingPayload( + GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *) BLEName.toCharArray(), + BLEName.length()); + ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, + BluetoothServiceNotifyUUID, + 16); + ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, + BluetoothServiceProgramUUID, + 16); + ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.setAdvertisingInterval(200); + ble.setAdvertisingTimeout(0); + ble.startAdvertising(); + + characteristicsHandle = characteristic.getValueHandle(); +} + +void BluetoothServiceNotify::onDataRead(GattReadAuthCallbackParams *params) +{ + if (params->handle == characteristicsHandle) { + } +} + +void BluetoothServiceNotify::send(uint16_t address, uint16_t value) +{ + if (ble.getGapState().connected) { + + uint8_t buffer[4]; + buffer[0] = HI16(address); + buffer[1] = LO16(address); + buffer[2] = HI16(value); + buffer[3] = LO16(value); + + ble.gattServer().notify( + characteristicsHandle, + (uint8_t *)buffer, sizeof(buffer)); + } +} \ No newline at end of file diff --git a/source/BluetoothServiceNotify.h b/source/BluetoothServiceNotify.h new file mode 100644 index 0000000..251f477 --- /dev/null +++ b/source/BluetoothServiceNotify.h @@ -0,0 +1,45 @@ + +#ifndef BLUETOOTH_SERVICE_NOTIFY_H +#define BLUETOOTH_SERVICE_NOTIFY_H + +//#include "ManagedString.h" +#include "ble/BLE.h" +#include "Interpreter.h" + +/*! + * @class BluetoothServiceNotify + */ +class BluetoothServiceNotify +{ +public: + + /*! + * Constructor. + * Create a representation of BluetoothServiceNotify + * @param interpreter Reference to an Interpreter instance + */ + BluetoothServiceNotify(Interpreter &interpreter); + + /*! + * Callback. Invoked when any of our attributes are read via BLE. + */ + void onDataRead(GattReadAuthCallbackParams *params); + + /*! + * Send data via BLE. + * @param address + * @param value + */ + void send(uint16_t address, uint16_t value); + +private: + + Interpreter &interpreter; + BLEDevice &ble; + + GattAttribute::Handle_t characteristicsHandle; + GattCharacteristic characteristic; + uint8_t characteristicsBuffer[16]; +}; + +#endif // BLUETOOTH_SERVICE_NOTIFY_H \ No newline at end of file diff --git a/source/BluetoothServiceProgram.cpp b/source/BluetoothServiceProgram.cpp new file mode 100644 index 0000000..1a329bd --- /dev/null +++ b/source/BluetoothServiceProgram.cpp @@ -0,0 +1,131 @@ +#include "BluetoothServiceProgram.h" +#include "MicroBit.h" +//#include "ble/UUID.h" +#include "Bytes.h" + +extern MicroBit uBit; + +BluetoothServiceProgram::BluetoothServiceProgram(Interpreter &_interpreter) : + interpreter(_interpreter), + ble(*uBit.ble), + characteristic( + BluetoothServiceProgramUUID, + (uint8_t *)&characteristicsBuffer, 0, sizeof(characteristicsBuffer), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ + ), + characteristicsBuffer() +{ + characteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK);//MICROBIT_BLE_SECURITY_LEVEL); + characteristic.setReadAuthorizationCallback(this, &BluetoothServiceProgram::onDataRead); + + GattCharacteristic *characteristics[] = { + &characteristic + }; + + GattService service( + BluetoothServiceProgramUUID, + characteristics, + sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + +// ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, BluetoothServiceProgramUUID, 16); +// ble.gap().updateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, BluetoothServiceProgramUUID,16); + + + characteristicsHandle = characteristic.getValueHandle(); + + ble.onDataWritten(this, &BluetoothServiceProgram::onDataWritten); +} + +void BluetoothServiceProgram::onDataWritten(const GattWriteCallbackParams *params) +{ + if (params->handle == characteristicsHandle) { + + const uint8_t *data = params->data; + const uint16_t len = params->len; + + if (len < 2*2) { + interpreter.status = INTERPRETER_KO_LEN_TOO_SHORT; + return; + } + + const uint16_t length = BYTES_TO_UINT16(data[0],data[1]); + + if (length == 0) { + // end of upload + + const uint16_t hash_sh = BYTES_TO_UINT16(data[2],data[3]); + const uint16_t hash_is = interpreter_calculate_hash(); + + // verify hash + if (hash_is != hash_sh) { + interpreter.status = INTERPRETER_KO_HASH_INVALID; + return; + } + + if (len != 2*2 + METHODS_COUNT*2) { + interpreter.status = INTERPRETER_KO_METHODS_TOO_SHORT; + return; + } + + // copy methods + for(int i=0, o=4; i= CODE_LEN) { + interpreter.status = INTERPRETER_KO_CODE_START_OUT_OF_BOUNDS; + return; + } + + if (position+length > CODE_LEN) { + interpreter.status = INTERPRETER_KO_CODE_STOP_OUT_OF_BOUNDS; + return; + } + + // copy length bytes to position + memcpy((void*)&interpreter.code[position], (const void*)&data[4], length); + } +} + +void BluetoothServiceProgram::onDataRead(GattReadAuthCallbackParams *params) +{ + if (params->handle == characteristicsHandle) { + + const uint16_t hash_is = interpreter_calculate_hash(); + + const uint8_t data[] = { + VERSION_MAJOR, + VERSION_MINOR, + HI16(CODE_LEN), + LO16(CODE_LEN), + HI16(hash_is), + LO16(hash_is), + interpreter.status + }; + + ble.gattServer().write( + characteristicsHandle, + data, sizeof(data)); + + } +} \ No newline at end of file diff --git a/source/BluetoothServiceProgram.h b/source/BluetoothServiceProgram.h new file mode 100644 index 0000000..2865b84 --- /dev/null +++ b/source/BluetoothServiceProgram.h @@ -0,0 +1,50 @@ +#ifndef BLUETOOTH_SERVICE_PROGRAM_H +#define BLUETOOTH_SERVICE_PROGRAM_H + +#include "ble/BLE.h" +#include "Interpreter.h" + +/*! + * @class BluetoothServiceProgram + */ +class BluetoothServiceProgram +{ +public: + + /*! + * Constructor. + * Create a representation of BluetoothServiceProgram + * @param interpreter + */ + BluetoothServiceProgram(Interpreter &interpreter); + + /*! + * Callback. Invoked when any of our attributes are written via BLE. + */ + void onDataWritten(const GattWriteCallbackParams *params); + + /*! + * Callback. Invoked when any of our attributes are read via BLE. + */ + void onDataRead(GattReadAuthCallbackParams *params); + +private: + + Interpreter &interpreter; + BLEDevice &ble; + + GattAttribute::Handle_t characteristicsHandle; + GattCharacteristic characteristic; + uint8_t characteristicsBuffer[CHARACTERISTICS_BUFFER_LEN]; +}; + + +const uint8_t BluetoothServiceProgramUUID[] = { + 0xff, 0x66, 0xdd, 0xee, + 0x25, 0x1d, + 0x47, 0x0a, + 0xa0, 0x62, + 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 +}; + +#endif // BLUETOOTH_SERVICE_PROGRAM_H \ No newline at end of file diff --git a/source/Bytes.h b/source/Bytes.h new file mode 100644 index 0000000..0f3faa6 --- /dev/null +++ b/source/Bytes.h @@ -0,0 +1,10 @@ +#ifndef BYTES_H +#define BYTES_H + +#include + +#define HI16(i) static_cast(((i)>>8)&0xff) +#define LO16(i) static_cast((i)&0xff) +#define BYTES_TO_UINT16(hi,lo) static_cast(((hi)<<8)|(lo)) + +#endif // BYTES_H \ No newline at end of file diff --git a/source/CalliopeDemo.cpp b/source/CalliopeDemo.cpp deleted file mode 100644 index f7fb3ca..0000000 --- a/source/CalliopeDemo.cpp +++ /dev/null @@ -1,781 +0,0 @@ -/** - * Calliope Demo Code. - * - * The code contains is the wrapper for the main functionality found after - * unpacking the Calliope mini. It contains the basic test functionality for - * in-production testing, as well as some small demo programs that can be - * selected using A and B buttons. - * - * - Oracle (press a button and get a smiley or sad face) - * - Rock, Paper, Scissors, Well - * - Love Meter (Pin 1 and 2) - * - Snake (adapted from the original microbit-samples) - * - ((game of life)) - * - ((proximty hearts)) - * - * The last two require the CalliopeDemoMaster to be compiled and put on - * an extra board. Then those games can be activated via Radio. - * - * @copyright (c) Calliope gGmbH. - * - * Licensed under the Apache Software License 2.0 (ASL 2.0) - * Portions (c) Copyright British Broadcasting Corporation under MIT License. - * - * @author Matthias L. Jugel - * @author Stephan Noller - * @author Franka Futterlieb - * @author Niranjan Rao - */ - -#include "MicroBit.h" -#include "CalliopeDemo.h" - -#ifndef COMPILE_FIRMWARE_MASTER - -const uint8_t full[25] = {1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1 -}; -const uint8_t dot[25] = {0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 -}; -const uint8_t small[25] = {0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, - 0, 1, 0, 1, 0, - 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0 -}; -const uint8_t large[25] = {1, 1, 1, 1, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 1, 1, 1, 1, -}; -const uint8_t arrow_left[25] = {0, 0, 1, 0, 0, - 0, 1, 0, 0, 0, - 1, 1, 1, 1, 1, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0 -}; -const uint8_t arrow_right[25] = {0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, - 1, 1, 1, 1, 1, - 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0 -}; -const uint8_t arrow_leftright[25] = {0, 0, 1, 0, 0, - 0, 1, 0, 1, 0, - 1, 0, 0, 0, 1, - 0, 1, 0, 1, 0, - 0, 0, 1, 0, 0 -}; -const uint8_t double_row[25] = {0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0, - 0, 1, 1, 0, 0 -}; -const uint8_t tick[25] = {0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, - 1, 0, 1, 0, 0, - 0, 1, 0, 0, 0 -}; -const uint8_t heart[25] = {0, 1, 0, 1, 0, - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, - 0, 1, 1, 1, 0, - 0, 0, 1, 0, 0 -}; -const uint8_t smiley[25] = {0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, - 0, 1, 1, 1, 0 -}; -const uint8_t sadly[25] = {0, 1, 0, 1, 0, - 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, - 1, 0, 0, 0, 1 -}; -const uint8_t rock[25] = {0, 0, 0, 0, 0, - 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, - 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0 -}; -const uint8_t scissors[25] = {1, 0, 0, 0, 1, - 0, 1, 0, 1, 0, - 0, 0, 1, 0, 0, - 0, 1, 0, 1, 0, - 1, 0, 0, 0, 1 -}; -const uint8_t well[25] = {0, 1, 1, 1, 0, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 0, 1, 1, 1, 0 -}; -const uint8_t flash[25] = {0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, - 1, 1, 1, 1, 1, - 0, 0, 1, 1, 0, - 0, 1, 1, 0, 0, - -}; -const uint8_t wave[7 * 5] = {0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 0, 0, 0, 0, - 1, 0, 0, 1, 0, 0, 1, - 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0 -}; - -const MicroBitImage Full(5, 5, full); -const MicroBitImage Dot(5, 5, dot); -const MicroBitImage SmallRect(5, 5, small); -const MicroBitImage LargeRect(5, 5, large); -const MicroBitImage ArrowLeft(5, 5, arrow_left); -const MicroBitImage ArrowRight(5, 5, arrow_right); -const MicroBitImage ArrowLeftRight(5, 5, arrow_leftright); -const MicroBitImage DoubleRow(5, 5, double_row); -const MicroBitImage Tick(5, 5, tick); -const MicroBitImage Heart(5, 5, heart); -const MicroBitImage Smiley(5, 5, smiley); -const MicroBitImage Sadly(5, 5, sadly); -const MicroBitImage Rock(5, 5, rock); -const MicroBitImage Scissors(5, 5, scissors); -const MicroBitImage Well(5, 5, well); -const MicroBitImage Flash(5, 5, flash); -const MicroBitImage Wave(7, 5, wave); - -MicroBit uBit; - -// DEMO 0 - 3 -static const int MAX_DEMOS = 3; -static const int DEFAULT_PAUSE = 300; - -volatile bool eventOK = false; -volatile static bool introEventSkip = false; - -void simpleEventHandler(MicroBitEvent event) { - (void) event; - eventOK = true; -} - -void leaveBeep() { - uBit.soundmotor.soundOn(784); - uBit.sleep(125); - uBit.soundmotor.soundOff(); -} - -void introSkipEventHandler(MicroBitEvent event) { - (void)event; - leaveBeep(); - uBit.display.stopAnimation(); - introEventSkip = true; - eventOK = true; -} - -static bool on = true; - -void blinkImage(const MicroBitImage &image, int interval, int rate) { - if (rate % interval == 0) { - if (on) uBit.display.printAsync(image, 0, 0, 0, 100); - else uBit.display.clear(); - on = !on; - } - uBit.sleep(100); -} - -void introBlinkImage(const MicroBitImage &image, int interval) { - int rate = 0; - do blinkImage(image, interval, ++rate); while (!eventOK); - uBit.display.clear(); -} - -void introAnimateImage(const MicroBitImage &image, int interval, int pos1, int pos2) { - int rate = 0; - bool on = true; - do { - if (++rate % interval == 0) { - uBit.display.clear(); - if (on) uBit.display.print(image, pos1, 0, 0, 200); - else uBit.display.print(image, pos2, 0, 0, 200); - on = !on; - } - uBit.sleep(100); - } while (!eventOK); - uBit.display.clear(); -} - -void dadadada() { - for (int i = 0; i < 3; i++) { - uBit.soundmotor.soundOn(392); - fiber_sleep(125); - uBit.soundmotor.soundOff(); - fiber_sleep(63); - } - uBit.soundmotor.soundOn(311); - fiber_sleep(1500); - uBit.soundmotor.soundOff(); - fiber_sleep(63); - for (int i = 0; i < 3; i++) { - uBit.soundmotor.soundOn(349); - fiber_sleep(125); - uBit.soundmotor.soundOff(); - fiber_sleep(63); - } - uBit.soundmotor.soundOn(294); - fiber_sleep(1500); - uBit.soundmotor.soundOff(); -} - -void freeFall(MicroBitEvent event) { - if (event.source == MICROBIT_ID_GESTURE && event.value == MICROBIT_ACCELEROMETER_EVT_FREEFALL) { - invoke(dadadada); - } -} - -void startSound() { - uBit.soundmotor.soundOn(262); - fiber_sleep(125); - uBit.soundmotor.soundOff(); - fiber_sleep(63); - uBit.soundmotor.soundOn(784); - fiber_sleep(500); - uBit.soundmotor.soundOff(); -} - -void showIntro() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_LONG_CLICK, introSkipEventHandler); - - invoke(startSound); - - uBit.display.scroll(DISPLAY_HELLO); - // press A - uBit.display.print("A"); - uBit.sleep(DEFAULT_PAUSE); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - introBlinkImage(ArrowLeft, 4); - if (introEventSkip) return; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - eventOK = false; - uBit.display.print(Tick); - uBit.sleep(DEFAULT_PAUSE); - uBit.display.clear(); - - // press B - uBit.display.print("B"); - uBit.sleep(DEFAULT_PAUSE); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - introBlinkImage(ArrowRight, 4); - if (introEventSkip) return; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - eventOK = false; - uBit.display.print(Tick); - uBit.sleep(DEFAULT_PAUSE); - uBit.display.clear(); - - // press A+B - uBit.display.scroll("A+B"); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - introBlinkImage(ArrowLeftRight, 4); - if (introEventSkip) return; - leaveBeep(); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - eventOK = false; - uBit.display.print(Tick); - uBit.sleep(DEFAULT_PAUSE); - uBit.display.clear(); - - // shake - uBit.display.scroll(DISPLAY_SHAKE); - uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, simpleEventHandler); - introAnimateImage(DoubleRow, 2, -1, 2); - if (introEventSkip) return; - uBit.messageBus.ignore(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, simpleEventHandler); - eventOK = false; - uBit.display.print(Tick); - uBit.sleep(DEFAULT_PAUSE); - uBit.display.clear(); - uBit.display.scroll(DISPLAY_SUPER); - - // show heart and change RGB led colors randomly - - on = true; - for (int rate = 0; rate < 25; rate++) { - if (introEventSkip) return; - blinkImage(Heart, 2, rate); - uBit.seedRandom(); - if (rate % 2 == 0) { - int r = uBit.random(DEFAULT_PAUSE); - if (r < 100) uBit.rgb.setColour(0xFF, 0xA5, 0x00, 0x00); - else if (r < 200) uBit.rgb.setColour(0x00, 0xFF, 0x00, 0x00); - else if (r < 300) uBit.rgb.setColour(0xFF, 0xA5, 0x00, 0x000); - } - uBit.sleep(100); - } - uBit.rgb.off(); - uBit.display.clear(); - - if (introEventSkip) return; - uBit.display.print(Smiley); - uBit.sleep(1000); -} - - -// MENU handling -volatile state_t state = Intro; - -static int selectedDemo = 0; - -void menuDown(MicroBitEvent event) { - (void)event; - selectedDemo--; - if (selectedDemo < 0) selectedDemo = MAX_DEMOS; - uBit.display.print(ManagedString(selectedDemo + 1)); -} - -void menuUp(MicroBitEvent event) { - (void)event; - selectedDemo++; - if (selectedDemo > MAX_DEMOS) selectedDemo = 0; - uBit.display.print(ManagedString(selectedDemo + 1)); -} - -void menuAnimateEnter() { - uBit.display.print(Dot, 0, 0, 0, 200); - uBit.display.print(SmallRect, 0, 0, 0, 200); - uBit.display.print(LargeRect, 0, 0, 0, 200); - uBit.display.clear(); -} - -void menuAnimateLeave() { - uBit.display.print(LargeRect, 0, 0, 0, 200); - uBit.display.print(SmallRect, 0, 0, 0, 200); - uBit.display.print(Dot, 0, 0, 0, 200); - uBit.display.clear(); -} - -// Oracle Demo -void leaveOracle(MicroBitEvent event) { - (void)event; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveOracle); - leaveBeep(); - state = Menu; - eventOK = true; -} - -void oracle() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveOracle); - - uBit.display.scroll(DISPLAY_ORACLE); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, simpleEventHandler); - do { - eventOK = false; - introBlinkImage(ArrowLeft, 4); - if (state != Oracle) break; - for (int i = 0; i < 5; i++) { - uBit.display.print(Dot, 0, 0, 0, DEFAULT_PAUSE); - uBit.display.clear(); - uBit.sleep(200); - } - int r = uBit.random(100); - if (r < 50) uBit.display.print(Smiley); - else uBit.display.print(Sadly); - uBit.sleep(3000); - uBit.display.clear(); - } while (state == Oracle); -} - -// Rock Paper Scissors Well Demo -void leaveRockPaperScissors(MicroBitEvent event) { - (void)event; - uBit.messageBus.ignore(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, simpleEventHandler); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveRockPaperScissors); - leaveBeep(); - state = Menu; - eventOK = true; -} - -void rockPaperScissors() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveRockPaperScissors); - - uBit.display.print(Rock, 0, 0, 0, DEFAULT_PAUSE * 2); - uBit.display.print(Full, 0, 0, 0, DEFAULT_PAUSE * 2); - uBit.display.print(Scissors, 0, 0, 0, DEFAULT_PAUSE * 2); - uBit.display.print(Well, 0, 0, 0, DEFAULT_PAUSE * 2); - uBit.display.clear(); - uBit.sleep(DEFAULT_PAUSE); - - uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, simpleEventHandler); - do { - eventOK = false; - introAnimateImage(DoubleRow, 2, -1, 2); - if (state != RockPaperScissors) break; - int r = uBit.random(400); - if (r < 100) uBit.display.print(Rock); - else if (r < 200) uBit.display.print(Full); - else if (r < 300) uBit.display.print(Scissors); - else uBit.display.print(Well); - uBit.sleep(3000); - uBit.display.clear(); - } while (state == RockPaperScissors); -} - -// Love Meter Demo -int touch_p1 = 0; -int touch_p2 = 0; - -// a little co-routing that does the measuring while we are waiting for both pins touched -void loveMeterMeasuring() { - touch_p1 = uBit.io.P1.getAnalogValue(); - touch_p2 = uBit.io.P2.getAnalogValue(); - do { - fiber_sleep(300); - int p1 = uBit.io.P1.getAnalogValue(); - int p2 = uBit.io.P2.getAnalogValue(); - eventOK = (abs(touch_p1 - p1) > 20 && abs(touch_p2 - p2) > 20); - } while (state == LoveMeter); -} - -void leaveLoveMeter(MicroBitEvent event) { - (void)event; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveLoveMeter); - leaveBeep(); - state = Menu; - eventOK = true; -} - -void loveMeter() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveLoveMeter); - - uBit.display.print(Heart, 0, 0, 0, DEFAULT_PAUSE); - invoke(loveMeterMeasuring); - - uBit.io.P1.isTouched(); - uBit.io.P2.isTouched(); - - do { - eventOK = false; - introAnimateImage(Wave, 2, -1, -2); - if (state != LoveMeter) break; - int p1 = uBit.io.P1.getAnalogValue(); - int p2 = uBit.io.P2.getAnalogValue(); - int v1 = (int) (p1 * 9.0 / 1023.0); - int v2 = (int) (p2 * 9.0 / 1023.0); - int v3 = 9 - abs(v1 - v2); - uBit.display.print(v3); - uBit.sleep(1000); - if (v3 > 6) { - uBit.display.print(Heart); - uBit.soundmotor.soundOn(1000); - uBit.sleep(100); - uBit.soundmotor.soundOn(2000); - uBit.sleep(100); - uBit.soundmotor.soundOn(3000); - uBit.sleep(300); - uBit.soundmotor.soundOff(); - uBit.sleep(4000); - } else { - uBit.display.print(Flash); - uBit.soundmotor.soundOn(1000); - uBit.sleep(100); - uBit.soundmotor.soundOn(500); - uBit.sleep(100); - uBit.soundmotor.soundOn(100); - uBit.sleep(300); - uBit.soundmotor.soundOff(); - uBit.sleep(4000); - } - uBit.display.clear(); - } while (state == LoveMeter); -} - -// Snake Demo -void leaveSnake(MicroBitEvent event) { - (void)event; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveSnake); - leaveBeep(); - state = Menu; - eventOK = true; -} - -void runSnake() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, leaveLoveMeter); - eventOK = false; - snake(); -} - -bool specialAttachmentActive = false; -bool foundPartner = false; -int specialAttachmentTransmitPower = 1; - -bool takeOverActive = false; -bool takeOverState = false; -uint8_t neighbors = 0; -uint8_t neighborIndex = 0; - -void onData(MicroBitEvent event) { - (void)event; - PacketBuffer packet = uBit.radio.datagram.recv(); - uBit.serial.send(packet); - - if (specialAttachmentActive) { - if (packet.getByte(0) == 'E') { - specialAttachmentTransmitPower = min(packet.getByte(1) - '0', 7); - state = Menu; - specialAttachmentActive = false; - selectedDemo = 0; - // proxy end of game - uBit.radio.setTransmitPower(7); - uBit.radio.datagram.send("E"); - } else { - const int signalStrength = packet.getRSSI(); - uBit.serial.send(ManagedString(signalStrength)); - uBit.serial.send("\r\n"); - foundPartner = packet.getByte(0) == '!' && signalStrength < 75; - } - } else if (takeOverActive) { - if (packet.getByte(0) == 'E') { - state = Menu; - takeOverActive = false; - neighborIndex = 99; - selectedDemo = 0; - // proxy end of game - uBit.radio.datagram.send("E"); - } else if (neighborIndex < 8) { - switch (packet.getByte(0)) { - case '0': - neighborIndex++; - break; - case '1': - neighbors++; - neighborIndex++; - break; - default: - break; - } - } - } else { - if (packet.getByte(0) == 'H') { - specialAttachmentActive = true; - state = Menu; - eventOK = true; - selectedDemo = 5; - uBit.radio.setTransmitPower(7); - uBit.radio.datagram.send("H"); - uBit.messageBus.send(MicroBitEvent(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE)); - } else if (packet.getByte(0) == 'S') { - takeOverActive = true; - state = Menu; - eventOK = true; - selectedDemo = 4; - uBit.messageBus.send(MicroBitEvent(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE)); - uBit.radio.setTransmitPower(7); - uBit.radio.datagram.send("S"); - } else if (packet.getByte(0) == 'E') { - // proxy end of game - takeOverActive = false; - specialAttachmentActive = false; - uBit.radio.setTransmitPower(7); - uBit.radio.datagram.send("E"); - } - } -} - -void liveOrDead() { - uBit.display.image.setPixelValue(1, 1, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(1, 2, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(1, 3, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(2, 1, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(2, 2, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(2, 3, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(3, 1, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(3, 2, (uint8_t) (takeOverState ? 255 : 0)); - uBit.display.image.setPixelValue(3, 3, (uint8_t) (takeOverState ? 255 : 0)); -} - -void takeOver() { - int displayMode = uBit.display.getDisplayMode(); - uBit.display.setDisplayMode(DISPLAY_MODE_GREYSCALE); - - neighborIndex = 0; - neighbors = 0; - takeOverState = uBit.random(100) > 20; - liveOrDead(); - - uBit.soundmotor.soundOn(262); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - uBit.sleep(63); - uBit.soundmotor.soundOn(784); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - - uBit.display.clear(); - while (state == TakeOver) { - unsigned long timeout = uBit.systemTime() + 5000; - uBit.serial.send(neighborIndex); - - int x = 0, y = 0; - uBit.display.image.setPixelValue(x, y, 64); - while (neighborIndex < 8 && timeout > uBit.systemTime()) { - uBit.sleep(200); - uBit.display.image.setPixelValue(x, y, 0); - if (y == 0 && x < 4) x++; - else if (x == 4 && y < 4) y++; - else if (y == 4 && x > 0) x--; - else if (x == 0 && y > 0) y--; - uBit.display.image.setPixelValue(x, y, 64); - } - uBit.display.image.setPixelValue(x, y, 0); - // only act if takeover is active - if (takeOverActive) { - if (takeOverState && (neighbors < 2 || neighbors > 3)) takeOverState = false; - else if (!takeOverState && neighbors == 3) takeOverState = true; - - liveOrDead(); - - neighbors = 0; - neighborIndex = 0; - uBit.radio.datagram.send(takeOverState ? "1" : "0"); - } - uBit.sleep(DEFAULT_PAUSE); - } - uBit.soundmotor.soundOn(784); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - uBit.sleep(63); - uBit.soundmotor.soundOn(262); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - - uBit.display.setDisplayMode((DisplayMode) displayMode); -} - -void specialAttachment() { - uBit.soundmotor.soundOn(262); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - uBit.sleep(63); - uBit.soundmotor.soundOn(784); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - - uBit.display.clear(); - while (state == SpecialAttachment) { - if (uBit.systemTime() % 1000 < 500) uBit.display.clear(); - else if (foundPartner) uBit.display.print(Heart); - else uBit.display.image.setPixelValue(2, 2, 255); - uBit.radio.setTransmitPower(specialAttachmentTransmitPower); - uBit.radio.datagram.send("!"); - uBit.sleep(DEFAULT_PAUSE); - } - - uBit.soundmotor.soundOn(784); - uBit.sleep(125); - uBit.soundmotor.soundOff(); - uBit.sleep(63); - uBit.soundmotor.soundOn(262); - uBit.sleep(125); - uBit.soundmotor.soundOff(); -} - -// Menu Selection -void menuSelect(MicroBitEvent event); - -void initializeMenu() { - uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, menuDown); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, menuUp); - uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, menuSelect); - uBit.display.print(ManagedString(selectedDemo + 1)); -} - -void menuSelect(MicroBitEvent event) { - (void)event; - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, menuDown); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, menuUp); - uBit.messageBus.ignore(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE, menuSelect); - - menuAnimateEnter(); - switch (selectedDemo) { - case 0: - state = Oracle; - oracle(); - break; - case 1: - state = RockPaperScissors; - rockPaperScissors(); - break; - case 2: - state = LoveMeter; - loveMeter(); - break; - case 3: - state = Snake; - runSnake(); - break; - case 4: - state = TakeOver; - takeOver(); - break; - case 5: - state = SpecialAttachment; - specialAttachment(); - break; - default: - state = Menu; - uBit.display.scroll(DISPLAY_ERROR); - selectedDemo = 0; - uBit.messageBus.send(MicroBitEvent(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK)); - break; - } - menuAnimateLeave(); - initializeMenu(); -} - -int main() { - // Initialise the micro:bit runtime. - uBit.init(); - uBit.serial.baud(115200); - uBit.serial.send("Calliope Demo v1.0\r\n"); - // disabled the test board procedure as it may confuse users who burn the initial firmware - // call the test board procedure, will return if done already - testBoard(); - - // initialize random - uBit.seedRandom(); - - showIntro(); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_LONG_CLICK, introSkipEventHandler); - uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_EVT_ANY, freeFall); - - state = Menu; - - uBit.display.clear(); - on = true; - for (int i = 0; i < 5; i++) { - blinkImage(Dot, 2, 2); - uBit.sleep(DEFAULT_PAUSE); - } - uBit.display.clear(); - - // init take over - uBit.messageBus.listen(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, onData); - uBit.radio.enable(); - uBit.radio.setGroup(227); - - initializeMenu(); - - while (true) uBit.sleep(100); -} - -#endif diff --git a/source/CalliopeDemo.h b/source/CalliopeDemo.h deleted file mode 100644 index d696e20..0000000 --- a/source/CalliopeDemo.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CALLOPE_DEMO_H -#define CALLOPE_DEMO_H - - -// MENU HANDLING -typedef volatile enum State { - Intro = 0, - Menu, - Oracle, - RockPaperScissors, - LoveMeter, - Snake, - SpecialAttachment = 55, - TakeOver = 99, -} state_t; - -extern const MicroBitImage Full; -extern const MicroBitImage Tick; - -void testBoard(); -void snake(); - -#ifdef YOTTA_CFG_COMPILE_MASTER -#define COMPILE_FIRMWARE_MASTER 0 -#endif - -// localization -#ifdef YOTTA_CFG_USE_ENGLISH -#define DISPLAY_HELLO "Hello!" -#define DISPLAY_SHAKE "SHAKE" -#define DISPLAY_ORACLE "Oracle" -#define DISPLAY_SUPER "SUPER!" -#define DISPLAY_THEEND "THE END" -#define DISPLAY_ERROR "Oops?" -#else -#define DISPLAY_HELLO "Hallo!" -#define DISPLAY_SHAKE "SCHUETTELN" -#define DISPLAY_ORACLE "Orakel" -#define DISPLAY_SUPER "SUPER!" -#define DISPLAY_THEEND "ENDE" -#define DISPLAY_ERROR "Huch?" -#endif - -#endif diff --git a/source/CalliopeDemoMaster.cpp b/source/CalliopeDemoMaster.cpp deleted file mode 100644 index d0736f3..0000000 --- a/source/CalliopeDemoMaster.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Callipe Demo Master - * - * This is a special master program that can be flashed onto - * an extra board and used to activate the hidden games in the - * official firmware. - * - * Press A to activate Conways game of life in all minis found. - * Press B to acticate Priximity Hearts in all minis found. - * Press A+B to stop the games. - * - * The minis will proxy the start and stop codes. - * - * @copyright (c) Calliope gGmbH. - * - * Licensed under the Apache Software License 2.0 (ASL 2.0) - * - * @author Matthias L. Jugel - */ - -#include "MicroBit.h" -#include "CalliopeDemo.h" -#include "nrf_delay.h" -#include "nrf_gpio.h" - - -#ifdef COMPILE_FIRMWARE_MASTER - -MicroBit uBit; - - -void buttonA(MicroBitEvent event) { - (void)event; - uBit.serial.send("START\r\n"); - uBit.radio.datagram.send("S"); -} - -void buttonB(MicroBitEvent event) { - (void)event; - uBit.serial.send("START 1\r\n"); - uBit.radio.datagram.send("H1"); -} - -void buttonB1(MicroBitEvent event) { - (void)event; - uBit.serial.send("RANDOM SEED\r\n"); - uBit.radio.datagram.send(uBit.random(100) > 20 ? "1" : "0"); -} - -void buttonAB(MicroBitEvent event) { - (void)event; - uBit.serial.send("END\r\n"); - uBit.radio.datagram.send("E"); -} - - -void onData(MicroBitEvent event) { - (void)event; - PacketBuffer packet = uBit.radio.datagram.recv(); - uBit.serial.send("RECEIVED\r\n"); - uBit.serial.send(packet); - uBit.display.print(packet); -} - -int main() { - uBit.init(); - - uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, buttonA); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, buttonB); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_LONG_CLICK, buttonB1); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_AB, MICROBIT_BUTTON_EVT_CLICK, buttonAB); - - uBit.messageBus.listen(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM, onData); - - if(uBit.radio.enable() == MICROBIT_OK) uBit.display.scroll("RDY"); - uBit.radio.setGroup(227); - - while(true) uBit.sleep(100); -} - -#endif diff --git a/source/Images.cpp b/source/Images.cpp new file mode 100644 index 0000000..90e2506 --- /dev/null +++ b/source/Images.cpp @@ -0,0 +1,159 @@ +#include "Images.h" + +static const uint8_t pixel_full[25] = { + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 +}; +static const uint8_t pixel_dot[25] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; +static const uint8_t pixel_small[25] = { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0 +}; +static const uint8_t pixel_large[25] = { + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, +}; +static const uint8_t pixel_arrow_left[25] = { + 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0 +}; +static const uint8_t pixel_arrow_right[25] = { + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0 +}; +static const uint8_t pixel_arrow_leftright[25] = { + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0 +}; +static const uint8_t pixel_double_row[25] = { + 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0 +}; +static const uint8_t pixel_tick[25] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, + 1, 0, 1, 0, 0, + 0, 1, 0, 0, 0 +}; +static const uint8_t pixel_heart[25] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, + 0, 0, 1, 0, 0 +}; +static const uint8_t pixel_smiley[25] = { + 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0 +}; +static const uint8_t pixel_sadly[25] = { + 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1 +}; +static const uint8_t pixel_rock[25] = { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0 +}; +static const uint8_t pixel_scissors[25] = { + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1 +}; +static const uint8_t pixel_well[25] = { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0 +}; +static const uint8_t pixel_flash[25] = { + 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, +}; +static const uint8_t pixel_wave[7 * 5] = { + 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + +const MicroBitImage ImageSmiley(5, 5, pixel_smiley); +const MicroBitImage ImageSadly(5, 5, pixel_sadly); +const MicroBitImage ImageHeart(5, 5, pixel_heart); +const MicroBitImage ImageArrowLeft(5, 5, pixel_arrow_left); +const MicroBitImage ImageArrowRight(5, 5, pixel_arrow_right); +const MicroBitImage ImageArrowLeftRight(5, 5, pixel_arrow_leftright); +const MicroBitImage ImageFull(5, 5, pixel_full); +const MicroBitImage ImageDot(5, 5, pixel_dot); +const MicroBitImage ImageSmallRect(5, 5, pixel_small); +const MicroBitImage ImageLargeRect(5, 5, pixel_large); +const MicroBitImage ImageDoubleRow(5, 5, pixel_double_row); +const MicroBitImage ImageTick(5, 5, pixel_tick); +const MicroBitImage ImageRock(5, 5, pixel_rock); +const MicroBitImage ImageScissors(5, 5, pixel_scissors); +const MicroBitImage ImageWell(5, 5, pixel_well); +const MicroBitImage ImageFlash(5, 5, pixel_flash); +const MicroBitImage ImageWave(7, 5, pixel_wave); + +const MicroBitImage *images[17] = { + &ImageSmiley, + &ImageSadly, + &ImageHeart, + &ImageArrowLeft, + &ImageArrowRight, + &ImageArrowLeftRight, + &ImageFull, + &ImageDot, + &ImageSmallRect, + &ImageLargeRect, + &ImageDoubleRow, + &ImageTick, + &ImageRock, + &ImageScissors, + &ImageWell, + &ImageFlash, + &ImageWave, +}; diff --git a/source/Images.h b/source/Images.h new file mode 100644 index 0000000..a054fb7 --- /dev/null +++ b/source/Images.h @@ -0,0 +1,26 @@ +#ifndef IMAGES_H +#define IMAGES_H + +#include "MicroBitImage.h" + +extern const MicroBitImage *images[17]; + +extern const MicroBitImage ImageSmiley; +extern const MicroBitImage ImageSadly; +extern const MicroBitImage ImageHeart; +extern const MicroBitImage ImageArrowLeft; +extern const MicroBitImage ImageArrowRight; +extern const MicroBitImage ImageArrowLeftRight; +extern const MicroBitImage ImageFull; +extern const MicroBitImage ImageDot; +extern const MicroBitImage ImageSmallRect; +extern const MicroBitImage ImageLargeRect; +extern const MicroBitImage ImageDoubleRow; +extern const MicroBitImage ImageTick; +extern const MicroBitImage ImageRock; +extern const MicroBitImage ImageScissors; +extern const MicroBitImage ImageWell; +extern const MicroBitImage ImageFlash; +extern const MicroBitImage ImageWave; + +#endif // IMAGES_H \ No newline at end of file diff --git a/source/Instruction.cpp b/source/Instruction.cpp new file mode 100644 index 0000000..27f3043 --- /dev/null +++ b/source/Instruction.cpp @@ -0,0 +1,792 @@ +#include +#include "Instruction.h" +#include "MicroBit.h" +#include "Images.h" +#include "BluetoothServiceNotify.h" + +#define UNUSED __attribute__((unused)) + +extern MicroBit uBit; +extern BluetoothServiceNotify *notify; + +typedef struct { + InterpreterStatus error; + int32_t *value; +} Register; + +static Register register_read(Slice &code, RunState &state) +{ + Register result = {}; + + if (slice_available(code) < 1) { + result.error = INTERPRETER_KO_INSTRUCTION_INVALID; + return result; + } + + const uint8_t reg = slice_read8(code); + + if (reg >= REGISTER_COUNT) { + result.error = INTERPRETER_KO_INSTRUCTION_INVALID; + return result; + } + + result.error = INTERPRETER_OK; + result.value = &state.reg[reg]; + + return result; +} + +static void compare(int32_t a, int32_t b, RunState &state) +{ + state.cs = + (a == b) ? COMPARED_EQ : + ((a < b) ? COMPARED_LT : COMPARED_GT); +} + +static void ret(Slice &code, Interpreter &interpreter UNUSED, RunState &state, bool comparison) +{ + state.pc = comparison ? state.stack : code.position; + state.stack = code.stop; +} + +static void bra(Slice &code, Interpreter &interpreter, RunState &state, bool comparison) +{ + if (slice_available(code) < 1) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const uint8_t value = slice_read8(code); + const int8_t offset = value; + + uBit.sleep(5); + + state.pc = comparison ? code.position + offset : code.position; +} + +void instruction_ret(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + true); +} + +void instruction_req(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs == COMPARED_EQ); +} + +void instruction_rne(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs != COMPARED_EQ); +} + +void instruction_rgt(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs == COMPARED_GT); +} + +void instruction_rlt(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs == COMPARED_LT); +} + +void instruction_rge(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs == COMPARED_GT || + state.cs == COMPARED_EQ); +} + +void instruction_rle(Slice &code, Interpreter &interpreter, RunState &state) +{ + ret(code, interpreter, state, + state.cs == COMPARED_LT || + state.cs == COMPARED_EQ); +} + +void instruction_jsr(Slice &code, Interpreter &interpreter, RunState &state) +{ + if (slice_available(code) < 1) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const uint8_t value = slice_read8(code); + const int8_t offset = value; + const uint16_t address = code.position + offset; + + // this should be push + state.stack = code.position; + + state.pc = address; +} + + +void instruction_bra(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + true); +} + +void instruction_beq(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs == COMPARED_EQ); +} + +void instruction_bne(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs != COMPARED_EQ); +} + +void instruction_bgt(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs == COMPARED_GT); +} + +void instruction_blt(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs == COMPARED_LT); +} + +void instruction_bge(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs == COMPARED_GT || + state.cs == COMPARED_EQ); +} + +void instruction_ble(Slice &code, Interpreter &interpreter, RunState &state) +{ + bra(code, interpreter, state, + state.cs == COMPARED_LT || + state.cs == COMPARED_EQ); +} + +void instruction_bra16(Slice &code, Interpreter &interpreter, RunState &state) +{ + if (slice_available(code) < 2) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const int16_t value = slice_read16(code); + + state.pc = code.position + value; +} + + + + +void instruction_cmp(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register ra = register_read(code, state); + if (ra.error) { + interpreter.status = ra.error; + return; + } + + Register rb = register_read(code, state); + if (rb.error) { + interpreter.status = rb.error; + return; + } + + compare(*ra.value, *rb.value, state); + + state.pc = code.position; +} + +void instruction_cmpi(Slice &code, Interpreter &interpreter, RunState &state) +{ + if (slice_available(code) < 2) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const int32_t a = slice_read16(code); + + Register rb = register_read(code, state); + if (rb.error) { + interpreter.status = rb.error; + return; + } + + compare(a, *rb.value, state); + + state.pc = code.position; +} + +void instruction_mov(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register src = register_read(code, state); + if (src.error) { + interpreter.status = src.error; + return; + } + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + const int32_t value = *src.value; + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_movi(Slice &code, Interpreter &interpreter, RunState &state) +{ + if (slice_available(code) < 2) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const int32_t value = slice_read16(code); + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + + +void instruction_add(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register src = register_read(code, state); + if (src.error) { + interpreter.status = src.error; + return; + } + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + const int32_t value = *dst.value + *src.value; + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_sub(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register src = register_read(code, state); + if (src.error) { + interpreter.status = src.error; + return; + } + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + const int32_t value = *dst.value - *src.value; + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_mul(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register src = register_read(code, state); + if (src.error) { + interpreter.status = src.error; + return; + } + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + const int32_t value = *dst.value * *src.value; + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_div(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register src = register_read(code, state); + if (src.error) { + interpreter.status = src.error; + return; + } + + Register dst = register_read(code, state); + if (dst.error) { + interpreter.status = dst.error; + return; + } + + if (*src.value == 0) { + interpreter.status = INTERPRETER_KO_DIVISION_BY_ZERO; + return; + } + + const int32_t value = *dst.value / *src.value; + + *dst.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + + +void instruction_sleep(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register time = register_read(code, state); + if (time.error) { + interpreter.status = time.error; + return; + } + + uBit.sleep(static_cast(*time.value)); + + state.pc = code.position; +} + +void instruction_random(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.random(*r.value); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_time(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.systemTime(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + + +void instruction_temperature(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.thermometer.getTemperature(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_noise(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int value = uBit.io.P21.getAnalogValue(); + + if (value > 512) { + // we do not support double and int32 should be enough + const int32_t gauge = static_cast((log2(value - 511) * 4) / 9); + + *r.value = gauge; + } else { + *r.value = 0; + } + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_brightness(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.display.readLightLevel(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + + +void instruction_button(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + int32_t value; + + switch(*r.value) { + case 0x01: + value = uBit.buttonA.isPressed(); + break; + case 0x02: + value = uBit.buttonB.isPressed(); + break; + case 0x03: + value = uBit.buttonAB.isPressed(); + break; + default: + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +static uint8_t pins[] = { 12, 0, 1, 16 }; + +void instruction_pin(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t i = *r.value; + if (i < 0 || static_cast(i) >= sizeof(pins)) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const int32_t value = uBit.io.pin[pins[i]].isTouched(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + + +void instruction_display_clear(Slice &code, Interpreter &interpreter UNUSED, RunState &state) +{ + uBit.display.clear(); + + state.pc = code.position; +} + +void instruction_display_show_number(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register number = register_read(code, state); + if (number.error) { + interpreter.status = number.error; + return; + } + + const int value = *number.value; + + uBit.display.print(ManagedString(value)); + + state.pc = code.position; +} + +void instruction_display_show_image(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register image = register_read(code, state); + if (image.error) { + interpreter.status = image.error; + return; + } + + const int32_t i = *image.value; + if (static_cast(i) >= sizeof(images)/sizeof(images[0])) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + uBit.display.print(*images[i]); + + state.pc = code.position; +} + +void instruction_display_show_grid(Slice &code, Interpreter &interpreter, RunState &state) +{ + uint8_t pixels[25]; + + if (slice_available(code) < sizeof(pixels)) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + for(unsigned int i=0; i code.stop) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + // Important: The mind numbingly stupid implementation of ManagedString + // requires a null terminated string (despite the given length). + // So make sure to also send the 0 byte. For security reasons we force it. + code.buffer[code.position+len-1] = 0; + + const ManagedString text = ManagedString((char*)&code.buffer[code.position]); + + uBit.display.scroll(ManagedString(text)); + + state.pc = code.position + len; +} + + +void instruction_rgb_off(Slice &code, Interpreter &interpreter UNUSED, RunState &state) +{ + // if the rg.off is called to frequently it begins to flicker + if (uBit.rgb.isOn()) { + uBit.rgb.off(); + } + + state.pc = code.position; +} + +void instruction_rgb_on(Slice &code, Interpreter &interpreter, RunState &state) +{ + if (slice_available(code) < 4) { + interpreter.status = INTERPRETER_KO_INSTRUCTION_INVALID; + return; + } + + const uint8_t r = slice_read8(code); + const uint8_t g = slice_read8(code); + const uint8_t b = slice_read8(code); + const uint8_t w = slice_read8(code); + + uBit.rgb.setColour(r, g, b, w); + + state.pc = code.position; +} + + +void instruction_sound_off(Slice &code, Interpreter &interpreter UNUSED, RunState &state) +{ + uBit.soundmotor.soundOff(); + + state.pc = code.position; +} + +void instruction_sound_on(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register freq = register_read(code, state); + if (freq.error) { + interpreter.status = freq.error; + return; + } + + uBit.soundmotor.soundOn(static_cast(*freq.value)); + + state.pc = code.position; +} + + +void instruction_gesture(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const uint16_t value = uBit.accelerometer.getGesture(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_pitch(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.accelerometer.getPitch(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_roll(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + const int32_t value = uBit.accelerometer.getRoll(); + + *r.value = value; + + compare(0, value, state); + + state.pc = code.position; +} + +void instruction_position(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register x = register_read(code, state); + if (x.error) { + interpreter.status = x.error; + return; + } + Register y = register_read(code, state); + if (y.error) { + interpreter.status = y.error; + return; + } + Register z = register_read(code, state); + if (z.error) { + interpreter.status = z.error; + return; + } + + *x.value = uBit.accelerometer.getX(); + *y.value = uBit.accelerometer.getY(); + *z.value = uBit.accelerometer.getZ(); + + state.pc = code.position; +} + + +void instruction_notify(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register address = register_read(code, state); + if (address.error) { + interpreter.status = address.error; + return; + } + + Register value = register_read(code, state); + if (value.error) { + interpreter.status = value.error; + return; + } + + if (notify != nullptr) { + // the protocol is allows only for the lower 16 bit + notify->send(static_cast(*address.value), static_cast(*value.value)); + } + + state.pc = code.position; +} + +void instruction_debug(Slice &code, Interpreter &interpreter, RunState &state) +{ + Register r = register_read(code, state); + if (r.error) { + interpreter.status = r.error; + return; + } + + uBit.serial.printf("debug: 0x%x\n\r", *r.value); + + state.pc = code.position; +} \ No newline at end of file diff --git a/source/Instruction.h b/source/Instruction.h new file mode 100644 index 0000000..e5e0ce6 --- /dev/null +++ b/source/Instruction.h @@ -0,0 +1,56 @@ +#ifndef INSTRUCTION_H +#define INSTRUCTION_H + +#include "Slice.h" +#include "Interpreter.h" + + +void instruction_ret(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_req(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rne(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rgt(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rlt(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rge(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rle(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_jsr(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_bra(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_beq(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_bne(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_bgt(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_blt(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_bge(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_ble(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_bra16(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_cmp(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_cmpi(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_mov(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_movi(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_add(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_sub(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_mul(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_div(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_sleep(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_random(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_time(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_temperature(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_noise(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_brightness(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_button(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_pin(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_display_clear(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_display_show_number(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_display_show_image(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_display_show_grid(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_display_show_text(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rgb_off(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_rgb_on(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_sound_off(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_sound_on(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_gesture(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_pitch(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_roll(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_position(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_notify(Slice &code, Interpreter &interpreter, RunState &state); +void instruction_debug(Slice &code, Interpreter &interpreter, RunState &state); + +#endif // INSTRUCTION_H \ No newline at end of file diff --git a/source/Interpreter.cpp b/source/Interpreter.cpp new file mode 100644 index 0000000..1778c78 --- /dev/null +++ b/source/Interpreter.cpp @@ -0,0 +1,465 @@ +#include "Interpreter.h" +#include "MicroBit.h" +#include "BluetoothServiceProgram.h" +#include "BluetoothServiceNotify.h" +#include "Instruction.h" + +extern MicroBit uBit; + +Interpreter interpreter; +BluetoothServiceNotify *notify; + +static uint16_t find_stop(InterpreterMethod method) +{ + for(uint8_t i = method + 1; i < METHODS_COUNT; i++) { + uint16_t stop = interpreter.methods[i]; + if (stop != METHOD_UNUSED) { + return stop; + } + } + return CODE_LEN; +} + +//extern void microbit_heap_print(); +#ifdef DEBUG +static uint8_t running = 0; +#endif +static void interpreter_run_method(InterpreterMethod method, int32_t r0 = 0, int32_t r1 = 0, int32_t r2 = 0) +{ + //microbit_heap_print(); + + uint16_t start = interpreter.methods[method]; + + if (start == METHOD_UNUSED) { + return; + } + + uint16_t stop = find_stop(method); + + #ifdef DEBUG + running += 1; + uint8_t id = running; + #endif + + // initialize interpreter state + RunState state = {}; + state.pc = start; + state.cs = COMPARED_EQ; + state.stack = stop; + memset(state.reg, 0, REGISTER_COUNT * sizeof(state.reg[0])); + state.reg[0] = r0; + state.reg[1] = r1; + state.reg[2] = r2; + + Slice code = slice_create(interpreter.code, start, stop); + + // LOG("%d: run 0x%x-0x%x start\n\r", id, start, stop); + + while(interpreter.status == INTERPRETER_OK) { + + uint8_t instruction = slice_read8(code); + + // LOG("%d: @0x%x ins 0x%x\n\r", id, code.position-1, instruction); + + switch(instruction) { + case INS_RET: + instruction_ret(code, interpreter, state); + break; + case INS_REQ: + instruction_req(code, interpreter, state); + break; + case INS_RNE: + instruction_rne(code, interpreter, state); + break; + case INS_RGT: + instruction_rgt(code, interpreter, state); + break; + case INS_RLT: + instruction_rlt(code, interpreter, state); + break; + case INS_RGE: + instruction_rge(code, interpreter, state); + break; + case INS_RLE: + instruction_rle(code, interpreter, state); + break; + + case INS_JSR: + instruction_jsr(code, interpreter, state); + break; + + case INS_BRA: + instruction_bra(code, interpreter, state); + break; + case INS_BEQ: + instruction_beq(code, interpreter, state); + break; + case INS_BNE: + instruction_bne(code, interpreter, state); + break; + case INS_BGT: + instruction_bgt(code, interpreter, state); + break; + case INS_BLT: + instruction_blt(code, interpreter, state); + break; + case INS_BGE: + instruction_bge(code, interpreter, state); + break; + case INS_BLE: + instruction_ble(code, interpreter, state); + break; + case INS_BRA16: + instruction_bra16(code, interpreter, state); + break; + + + case INS_CMP: + instruction_cmp(code, interpreter, state); + break; + case INS_CMPI: + instruction_cmpi(code, interpreter, state); + break; + case INS_MOV: + instruction_mov(code, interpreter, state); + break; + case INS_MOVI: + instruction_movi(code, interpreter, state); + break; + + case INS_ADD: + instruction_add(code, interpreter, state); + break; + case INS_SUB: + instruction_sub(code, interpreter, state); + break; + case INS_MUL: + instruction_mul(code, interpreter, state); + break; + case INS_DIV: + instruction_div(code, interpreter, state); + break; + + case INS_SLEEP: + instruction_sleep(code, interpreter, state); + break; + case INS_RANDOM: + instruction_random(code, interpreter, state); + break; + case INS_TIME: + instruction_time(code, interpreter, state); + break; + + case INS_TEMPERATURE: + instruction_temperature(code, interpreter, state); + break; + case INS_NOISE: + instruction_noise(code, interpreter, state); + break; + case INS_BRIGHTNESS: + instruction_brightness(code, interpreter, state); + break; + + case INS_BUTTON: + instruction_button(code, interpreter, state); + break; + case INS_PIN: + instruction_pin(code, interpreter, state); + break; + + case INS_DISPLAY_CLEAR: + instruction_display_clear(code, interpreter, state); + break; + case INS_DISPLAY_SHOW_NUMBER: + instruction_display_show_number(code, interpreter, state); + break; + case INS_DISPLAY_SHOW_IMAGE: + instruction_display_show_image(code, interpreter, state); + break; + case INS_DISPLAY_SHOW_GRID: + instruction_display_show_grid(code, interpreter, state); + break; + case INS_DISPLAY_SHOW_TEXT: + instruction_display_show_text(code, interpreter, state); + break; + + case INS_RGB_OFF: + instruction_rgb_off(code, interpreter, state); + break; + case INS_RGB_ON: + instruction_rgb_on(code, interpreter, state); + break; + + case INS_SOUND_OFF: + instruction_sound_off(code, interpreter, state); + break; + case INS_SOUND_ON: + instruction_sound_on(code, interpreter, state); + break; + + case INS_ACC_GESTURE: + instruction_gesture(code, interpreter, state); + break; + case INS_ACC_PITCH: + instruction_pitch(code, interpreter, state); + break; + case INS_ACC_ROLL: + instruction_roll(code, interpreter, state); + break; + case INS_ACC_POSITION: + instruction_position(code, interpreter, state); + break; + + case INS_NOTIFY: + instruction_notify(code, interpreter, state); + break; + case INS_DEBUG: + instruction_debug(code, interpreter, state); + break; + + default: + interpreter.status = INTERPRETER_KO_INSTRUCTION_UNKNOWN; + } + + code.position = state.pc; + + if (slice_available(code) < 1) { + LOG("%d: run 0x%x-0x%x stop\n\r", id, start, stop); + return; + } + } +} + +static void interpreter_reset_hardware() +{ + uBit.rgb.off(); + uBit.display.clear(); + uBit.soundmotor.soundOff(); +} + +static void interpreter_startup_sound() +{ + uBit.soundmotor.soundOn(262); + uBit.sleep(125); + uBit.soundmotor.soundOff(); + + uBit.sleep(63); + + uBit.soundmotor.soundOn(784); + uBit.sleep(500); + uBit.soundmotor.soundOff(); +} + +static void interpreter_on_button(MicroBitEvent event) +{ + uint8_t source; + switch(event.source) { + case MICROBIT_ID_BUTTON_A: source = 1; break; + case MICROBIT_ID_BUTTON_B: source = 2; break; + case MICROBIT_ID_BUTTON_AB: source = 3; break; + default: return; + } + + LOG("button 0x%x 0x%x start\n\r", source, event.value); + + interpreter_run_method(METHOD_ON_BUTTON, source, event.value); + + LOG("button 0x%x 0x%x stop\n\r", source, event.value); +} + +static void interpreter_on_pin(MicroBitEvent event) +{ + uint8_t source; + switch(event.source) { + case MICROBIT_ID_IO_P12: source = 0; break; + case MICROBIT_ID_IO_P0: source = 1; break; + case MICROBIT_ID_IO_P1: source = 2; break; + case MICROBIT_ID_IO_P16: source = 3; break; + default: return; + } + + LOG("pin 0x%x 0x%x start\n\r", source, event.value); + + interpreter_run_method(METHOD_ON_PIN, source, event.value); + + LOG("pin 0x%x 0x%x stop\n\r", source, event.value); +} + +static void interpreter_on_gesture(MicroBitEvent event) +{ + LOG("gesture 0x%x 0x%x start\n\r", event.source, event.value); + + interpreter_run_method(METHOD_ON_GESTURE, event.source, event.value); + + LOG("gesture 0x%x 0x%x stop\n\r", event.source, event.value); + } + +// static void interpreter_event(MicroBitEvent event) +// { +// if (event.source == MICROBIT_ID_GESTURE && event.value == MICROBIT_ACCELEROMETER_EVT_SHAKE) { +// interpreter_on_gesture(event); +// } else + +// if (event.source == MICROBIT_ID_BUTTON_A && event.value == MICROBIT_BUTTON_EVT_CLICK) { +// interpreter_on_button(event); +// } else +// if (event.source == MICROBIT_ID_BUTTON_B && event.value == MICROBIT_BUTTON_EVT_CLICK) { +// interpreter_on_button(event); +// } else +// if (event.source == MICROBIT_ID_BUTTON_AB && event.value == MICROBIT_BUTTON_EVT_CLICK) { +// interpreter_on_button(event); +// } else + +// if (event.source == MICROBIT_ID_IO_P12 && event.value == MICROBIT_PIN_EVENT_ON_TOUCH) { // P0 +// interpreter_on_pin(event); +// } else +// if (event.source == MICROBIT_ID_IO_P0 && event.value == MICROBIT_PIN_EVENT_ON_TOUCH) { // P1 +// interpreter_on_pin(event); +// } else +// if (event.source == MICROBIT_ID_IO_P1 && event.value == MICROBIT_PIN_EVENT_ON_TOUCH) { // P2 +// interpreter_on_pin(event); +// } else +// if (event.source == MICROBIT_ID_IO_P16 && event.value == MICROBIT_PIN_EVENT_ON_TOUCH) { // P3 +// interpreter_on_pin(event); +// } +// } + +static bool nothingToRun() +{ + for(uint8_t i = 0; i < METHODS_COUNT; i++) { + uint16_t method = interpreter.methods[i]; + if (method != METHOD_UNUSED) { + return false; + } + } + return true; +} + +static void interpreter_fiber() +{ + LOG("interpreter\n\r"); + + while(nothingToRun()) { + uBit.sleep(100); + } + + while(true) { + interpreter_reset_hardware(); + + LOG("start\n\r"); + + interpreter_run_method(METHOD_STARTUP); + + LOG("forever\n\r"); + + while(interpreter.status == INTERPRETER_OK) { + interpreter_run_method(METHOD_FOREVER); + uBit.sleep(10); + } + + LOG("rx\n\r"); + + while(interpreter.status != INTERPRETER_RD) { + uBit.sleep(10); + } + + interpreter.status = INTERPRETER_OK; + + LOG("reset\n\r"); + + interpreter_startup_sound(); + } +} + +void interpreter_reset() +{ + memset(interpreter.code, 0, CODE_LEN * sizeof(interpreter.code[0])); + memset(interpreter.methods, METHOD_UNUSED, METHODS_COUNT * sizeof(interpreter.methods[0])); + interpreter.status = INTERPRETER_OK; +} + + +static void interpreter_init() +{ + uBit.io.P12.isTouched(); + uBit.io.P0.isTouched(); + uBit.io.P1.isTouched(); + uBit.io.P16.isTouched(); + + // uBit.messageBus.listen( + // MICROBIT_ID_ANY, + // MICROBIT_EVT_ANY, + // interpreter_event); + + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + interpreter_on_button); + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + interpreter_on_button); + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + interpreter_on_button); + + + // Pin 0 + uBit.messageBus.listen( + MICROBIT_ID_IO_P12, + MICROBIT_EVT_ANY, + interpreter_on_pin); + + // Pin 1 + uBit.messageBus.listen( + MICROBIT_ID_IO_P0, + MICROBIT_EVT_ANY, + interpreter_on_pin); + + // Pin 2 + uBit.messageBus.listen( + MICROBIT_ID_IO_P1, + MICROBIT_EVT_ANY, + interpreter_on_pin); + + // Pin 3 + uBit.messageBus.listen( + MICROBIT_ID_IO_P16, + MICROBIT_EVT_ANY, + interpreter_on_pin); + + + uBit.messageBus.listen( + MICROBIT_ID_GESTURE, + MICROBIT_ACCELEROMETER_EVT_SHAKE, + interpreter_on_gesture); + + interpreter_reset(); + + // new BluetoothServiceDebug(interpreter); + new BluetoothServiceProgram(interpreter); + notify = new BluetoothServiceNotify(interpreter); + + uBit.sleep(200); +} + +void interpreter_run() +{ + interpreter_init(); + interpreter_fiber(); +} + +void interpreter_start() +{ + interpreter_init(); + invoke(interpreter_fiber); +} + +uint16_t interpreter_calculate_hash() +{ + return 0; +} diff --git a/source/Interpreter.h b/source/Interpreter.h new file mode 100644 index 0000000..f3e27eb --- /dev/null +++ b/source/Interpreter.h @@ -0,0 +1,138 @@ +#ifndef INTERPRETER_H +#define INTERPRETER_H + +#include + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 + +#define METHODS_COUNT 5 +#define CODE_LEN 256 +#define REGISTER_COUNT 5 +#define CHARACTERISTICS_BUFFER_LEN 40 + +//#define DEBUG + +#ifdef DEBUG +#define LOG(format, ...) uBit.serial.printf(format, ##__VA_ARGS__) +#else +#define LOG(format, ...) ; +#endif + +typedef enum { + INTERPRETER_OK = 0x00, + INTERPRETER_RX = 0x01, + INTERPRETER_RD = 0x02, + + INTERPRETER_KO_LEN_TOO_SHORT = 0x03, + INTERPRETER_KO_HASH_INVALID = 0x04, + INTERPRETER_KO_METHODS_TOO_SHORT = 0x05, + INTERPRETER_KO_METHODS_START_OUT_OF_BOUNDS = 0x06, + INTERPRETER_KO_METHODS_STOP_OUT_OF_BOUNDS = 0x07, + INTERPRETER_KO_CODE_TOO_SHORT = 0x08, + INTERPRETER_KO_CODE_START_OUT_OF_BOUNDS = 0x09, + INTERPRETER_KO_CODE_STOP_OUT_OF_BOUNDS = 0x0a, + INTERPRETER_KO_INSTRUCTION_UNKNOWN = 0x0b, + INTERPRETER_KO_INSTRUCTION_INVALID = 0x0c, + INTERPRETER_KO_DIVISION_BY_ZERO = 0x0d + +} InterpreterStatus; + +typedef struct { + InterpreterStatus status; + uint8_t code[CODE_LEN]; + uint16_t methods[METHODS_COUNT]; +} Interpreter; + +typedef enum { + COMPARED_EQ = 0, + COMPARED_GT = 1, + COMPARED_LT = 2 +} CompareState; + +typedef struct { + uint16_t pc; + uint16_t stack; // this should be a stack, for now one address is enough + int32_t reg[REGISTER_COUNT]; + CompareState cs; +} RunState; + +typedef enum { + METHOD_STARTUP = 0, + METHOD_FOREVER = 1, + METHOD_ON_BUTTON = 2, + METHOD_ON_PIN = 3, + METHOD_ON_GESTURE = 4 +} InterpreterMethod; + +#define METHOD_UNUSED 0xffff + +typedef enum { + INS_RET = 0x00, + INS_REQ = 0x01, + INS_RNE = 0x02, + INS_RGT = 0x03, + INS_RLT = 0x04, + INS_RGE = 0x05, + INS_RLE = 0x06, + + INS_JSR = 0x07, + + INS_BRA = 0x10, + INS_BEQ = 0x11, + INS_BNE = 0x12, + INS_BGT = 0x13, + INS_BLT = 0x14, + INS_BGE = 0x15, + INS_BLE = 0x16, + INS_BRA16 = 0x17, + + INS_CMP = 0x20, + INS_CMPI = 0x21, + INS_MOV = 0x22, + INS_MOVI = 0x23, + + INS_ADD = 0x30, + INS_SUB = 0x31, + INS_MUL = 0x32, + INS_DIV = 0x33, + + INS_SLEEP = 0x40, + INS_RANDOM = 0x41, + INS_TIME = 0x42, + + INS_TEMPERATURE = 0x50, + INS_NOISE = 0x51, + INS_BRIGHTNESS = 0x52, + + INS_BUTTON = 0x60, + INS_PIN = 0x61, + + INS_DISPLAY_CLEAR = 0x70, + INS_DISPLAY_SHOW_NUMBER = 0x71, + INS_DISPLAY_SHOW_IMAGE = 0x72, + INS_DISPLAY_SHOW_GRID = 0x73, + INS_DISPLAY_SHOW_TEXT = 0x74, + + INS_RGB_OFF = 0x80, + INS_RGB_ON = 0x81, + + INS_SOUND_OFF = 0x90, + INS_SOUND_ON = 0x91, + + INS_ACC_GESTURE = 0xa0, + INS_ACC_PITCH = 0xa1, + INS_ACC_ROLL = 0xa2, + INS_ACC_POSITION = 0xa3, + + INS_NOTIFY = 0xf0, + INS_DEBUG = 0xf1 + +} InterpreterInstruction; + +void interpreter_run(); +void interpreter_start(); +void interpreter_reset(); +uint16_t interpreter_calculate_hash(); + +#endif // INTERPRETER_H \ No newline at end of file diff --git a/source/Localization.h b/source/Localization.h new file mode 100644 index 0000000..88f4060 --- /dev/null +++ b/source/Localization.h @@ -0,0 +1,11 @@ +#ifndef LOCALIZATION_H +#define LOCALIZATION_H + +#define LS_DEMO_HELLO "Hi!" +#define LS_DEMO_BUTTON_A "A" +#define LS_DEMO_BUTTON_B "B" +#define LS_DEMO_BUTTON_AB "A+B" +//#define LS_DEMO_SHAKE "SCHUETTELN" +#define LS_DEMO_GREAT "OK!" + +#endif // LOCALIZATION_H \ No newline at end of file diff --git a/source/Main.cpp b/source/Main.cpp new file mode 100644 index 0000000..8c374a4 --- /dev/null +++ b/source/Main.cpp @@ -0,0 +1,142 @@ +#include "MicroBit.h" +#include "Storage.h" +#include "Images.h" +#include "RunTests.h" +#include "RunDemo.h" +#include "Menu.h" +#include "RunOracle.h" +#include "RunRockPaperScissors.h" +#include "RunMultiplication.h" +#include "RunVolumeMeter.h" +#include "Interpreter.h" +#include "nrf.h" + +MicroBit uBit; + +static void showNameHistogram(MicroBitDisplay &display) +{ + NRF_FICR_Type *ficr = NRF_FICR; + uint32_t n = ficr->DEVICEID[1]; + uint32_t ld = 1; + uint32_t d = MICROBIT_DFU_HISTOGRAM_HEIGHT; + uint32_t h; + + display.clear(); + for (uint32_t i = 0; i < MICROBIT_DFU_HISTOGRAM_WIDTH; i++) + { + h = (n % d) / ld; + + n -= h; + d *= MICROBIT_DFU_HISTOGRAM_HEIGHT; + ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT; + + for (uint32_t j = 0; j < h + 1; j++) + display.image.setPixelValue( + static_cast(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1), + static_cast(MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1), + 255); + } +} + +static inline void waitForever() +{ + while (true) { + uBit.sleep(1000); + } +} + +static void menuAnimateEnter() +{ + uBit.display.print(ImageDot); + uBit.sleep(200); + uBit.display.print(ImageSmallRect); + uBit.sleep(200); + uBit.display.print(ImageLargeRect); + uBit.sleep(200); + uBit.display.clear(); +} + +static void menuAnimateLeave() +{ + uBit.display.print(ImageLargeRect); + uBit.sleep(200); + uBit.display.print(ImageSmallRect); + uBit.sleep(200); + uBit.display.print(ImageDot); + uBit.sleep(200); + uBit.display.clear(); +} + +int main() +{ + uBit.init(); + uBit.accelerometer.updateSample(); + + uBit.serial.send("Calliope Demo v1.1\r\n"); + + if (hasStorageKey(KEY_INTERPRETER)) { + removeStorageKey(KEY_INTERPRETER); + + // minimize serial buffer +// uBit.serial.setTxBufferSize(0); + + showNameHistogram(uBit.display); + interpreter_run(); + + // not required - just to make it obvious this does not return + waitForever(); + } + + if (!hasStorageKey(KEY_TEST)) { + setStorageKey(KEY_TEST); + + tests_run(); + + // not required - just to make it obvious this does not return + waitForever(); + } + + if (!hasStorageKey(KEY_DEMO)) { + setStorageKey(KEY_DEMO); + + demo_run(); + } + + // start state + menustate_t state = MenuStateOracle; + + while (true) { + state = menuWaitForChoice(state); + switch (state) { + // 1 + case MenuStateOracle: + menuAnimateEnter(); + oracle_run(); + menuAnimateLeave(); + break; + // 2 + case MenuStateRockPaperScissors: + menuAnimateEnter(); + rockpaperscissors_run(); + menuAnimateLeave(); + break; + // 3 + case MenuStateMultiplication: + menuAnimateEnter(); + multiplication_run(); + menuAnimateLeave(); + break; + // 4 + case MenuStateVolumeMeter: + menuAnimateEnter(); + volumemeter_run(); + menuAnimateLeave(); + break; + // 5 + case MenuStateInterpreter: + setStorageKey(KEY_INTERPRETER); + uBit.reset(); + break; + } + } +} \ No newline at end of file diff --git a/source/Menu.cpp b/source/Menu.cpp new file mode 100644 index 0000000..95c81bd --- /dev/null +++ b/source/Menu.cpp @@ -0,0 +1,66 @@ +#include "Menu.h" +#include "MicroBit.h" + +extern MicroBit uBit; + + +menustate_t menuWaitForChoice(menustate_t start) +{ + menustate_t state = start; + while (true) { + + // if (state == MenuStateInterpreter) { + // showNameHistogram(uBit.display); + // // static const uint8_t pixels[25] = { + // // 0, 0, 1, 1, 1, + // // 0, 0, 0, 1, 1, + // // 0, 0, 1, 0, 1, + // // 0, 1, 0, 0, 0, + // // 1, 0, 0, 0, 0 + // // }; + // // const MicroBitImage Image(5, 5, pixels); + // // uBit.display.print(Image); + // } else { + // uBit.display.print(ManagedString(state)); + // } + + uBit.display.print(ManagedString(state)); + + // event loop + while (true) { + // button A + if (uBit.buttonA.isPressed()) { + while (uBit.buttonA.isPressed()) { + uBit.sleep(10); + } + + if (state > MenuStateMin) { + state = menustate_t(state - 1); + break; + } + } + + // button B + if (uBit.buttonB.isPressed()) { + while (uBit.buttonB.isPressed()) { + uBit.sleep(10); + } + + if (state < MenuStateMax) { + state = menustate_t(state + 1); + break; + } + } + + // gesture + if (uBit.accelerometer.getGesture() == MICROBIT_ACCELEROMETER_EVT_SHAKE) { + while (uBit.accelerometer.getGesture() == MICROBIT_ACCELEROMETER_EVT_SHAKE) { + uBit.sleep(10); + } + return state; + } + + uBit.sleep(10); + } + } +} \ No newline at end of file diff --git a/source/Menu.h b/source/Menu.h new file mode 100644 index 0000000..cfa3b0e --- /dev/null +++ b/source/Menu.h @@ -0,0 +1,16 @@ +#ifndef MENU_H +#define MENU_H + +typedef enum MenuState { + MenuStateOracle = 1, + MenuStateRockPaperScissors, + MenuStateMultiplication, + MenuStateVolumeMeter, + MenuStateInterpreter, + MenuStateMin = MenuStateOracle, + MenuStateMax = MenuStateInterpreter +} menustate_t; + +menustate_t menuWaitForChoice(menustate_t start); + +#endif // MENU_H \ No newline at end of file diff --git a/source/RunDemo.cpp b/source/RunDemo.cpp new file mode 100644 index 0000000..3b58caa --- /dev/null +++ b/source/RunDemo.cpp @@ -0,0 +1,123 @@ +#include "RunDemo.h" +#include "Images.h" +#include "Localization.h" +#include "MicroBit.h" +#include "Utils.h" + +extern MicroBit uBit; + +static void beep() +{ + uBit.soundmotor.soundOn(784); + uBit.sleep(125); + uBit.soundmotor.soundOff(); +} + +static void startSound() +{ + uBit.soundmotor.soundOn(262); + uBit.sleep(125); + uBit.soundmotor.soundOff(); + uBit.sleep(63); + uBit.soundmotor.soundOn(784); + uBit.sleep(500); + uBit.soundmotor.soundOff(); +} + +void demo_run() +{ + const uint16_t pause = 300; + + uBit.sleep(200); + + invoke(startSound); + + uBit.display.scroll(LS_DEMO_HELLO); + + // press A + uBit.display.print(LS_DEMO_BUTTON_A); + uBit.sleep(pause); + blinkImageUntilEvent( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + ImageArrowLeft); + uBit.display.print(ImageTick); + uBit.sleep(pause); + uBit.display.clear(); + + // press B + uBit.display.print(LS_DEMO_BUTTON_B); + uBit.sleep(pause); + blinkImageUntilEvent( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + ImageArrowRight); + uBit.display.print(ImageTick); + uBit.sleep(pause); + uBit.display.clear(); + + // press A+B + uBit.display.scroll(LS_DEMO_BUTTON_AB); + uBit.sleep(pause); + blinkImageUntilEvent( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + ImageArrowLeftRight); + beep(); + uBit.display.print(ImageTick); + uBit.sleep(pause); + uBit.display.clear(); + + // shake + //uBit.display.scroll(LS_DEMO_SHAKE); + blinkImageUntilEvent( + MICROBIT_ID_GESTURE, + MICROBIT_ACCELEROMETER_EVT_SHAKE, + ImageDoubleRow, + -1, + 2, + 100); + uBit.display.print(ImageTick); + uBit.sleep(pause); + uBit.display.clear(); + + uBit.display.scroll(LS_DEMO_GREAT); + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + for (uint8_t i = 0; i < 5 && !leave; i++) { + + int r = uBit.random(pause); + if (r < 100) { + uBit.rgb.setColour(0xFF, 0xA5, 0x00, 0x00); + } else if (r < 200) { + uBit.rgb.setColour(0x00, 0xFF, 0x00, 0x00); + } else if (r < 300) { + uBit.rgb.setColour(0x00, 0xA5, 0xFF, 0x00); + } + + blinkImage(ImageHeart, 0, 200); + } + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + uBit.rgb.off(); + uBit.display.clear(); + + uBit.display.print(ImageSmiley); + uBit.sleep(1000); +} \ No newline at end of file diff --git a/source/RunDemo.h b/source/RunDemo.h new file mode 100644 index 0000000..ff533fc --- /dev/null +++ b/source/RunDemo.h @@ -0,0 +1,6 @@ +#ifndef DEMO_H +#define DEMO_H + +void demo_run(); + +#endif // DEMO_H \ No newline at end of file diff --git a/source/RunLoveMeter.cpp b/source/RunLoveMeter.cpp new file mode 100644 index 0000000..a56cccf --- /dev/null +++ b/source/RunLoveMeter.cpp @@ -0,0 +1,92 @@ +#include "RunLoveMeter.h" +#include "MicroBit.h" +#include "Images.h" +#include "Utils.h" + +extern MicroBit uBit; + +static int touch_p1 = 0; +static int touch_p2 = 0; + +static void lovemeter_fiber() +{ + touch_p1 = uBit.io.P1.getAnalogValue(); + touch_p2 = uBit.io.P2.getAnalogValue(); + + while(!leave) { + uBit.sleep(300); + + int p1 = uBit.io.P1.getAnalogValue(); + int p2 = uBit.io.P2.getAnalogValue(); + + if (abs(touch_p1 - p1) > 20 && abs(touch_p2 - p2) > 20) { + event = true; + } + } +} + +void lovemeter_run() +{ + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + uBit.io.P1.isTouched(); + uBit.io.P2.isTouched(); + + uBit.display.print(ImageHeart); + + invoke(lovemeter_fiber); + + leave = false; + while (!leave) { + + event = false; + for (uint8_t i = 0; !leave && !event; i++) { + blinkImage(ImageWave, (i % 2) == 0 ? -1 : -2, 200); + } + + if (leave) { + break; + } + + int p1 = uBit.io.P1.getAnalogValue(); + int p2 = uBit.io.P2.getAnalogValue(); + int v1 = (int)(p1 * 9.0 / 1023.0); + int v2 = (int)(p2 * 9.0 / 1023.0); + int v3 = 9 - abs(v1 - v2); + uBit.display.print(v3); + + uBit.sleep(1000); + + if (v3 > 6) { + uBit.display.print(ImageHeart); + uBit.soundmotor.soundOn(1000); + uBit.sleep(100); + uBit.soundmotor.soundOn(2000); + uBit.sleep(100); + uBit.soundmotor.soundOn(3000); + uBit.sleep(300); + uBit.soundmotor.soundOff(); + uBit.sleep(4000); + } else { + uBit.display.print(ImageFlash); + uBit.soundmotor.soundOn(1000); + uBit.sleep(100); + uBit.soundmotor.soundOn(500); + uBit.sleep(100); + uBit.soundmotor.soundOn(100); + uBit.sleep(300); + uBit.soundmotor.soundOff(); + uBit.sleep(4000); + } + + uBit.display.clear(); + } + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); +} \ No newline at end of file diff --git a/source/RunLoveMeter.h b/source/RunLoveMeter.h new file mode 100644 index 0000000..a049003 --- /dev/null +++ b/source/RunLoveMeter.h @@ -0,0 +1,6 @@ +#ifndef RUN_LOVE_METER_H +#define RUN_LOVE_METER_H + +void lovemeter_run(); + +#endif // RUN_LOVE_METER_H \ No newline at end of file diff --git a/source/RunMultiplication.cpp b/source/RunMultiplication.cpp new file mode 100644 index 0000000..c41b429 --- /dev/null +++ b/source/RunMultiplication.cpp @@ -0,0 +1,102 @@ +#include "RunMultiplication.h" +#include "MicroBit.h" +#include "Utils.h" +#include "Images.h" + +extern MicroBit uBit; + +static const uint8_t pixel_multiplier[25] = { + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0 +}; +static const MicroBitImage ImageMultiplier(5, 5, pixel_multiplier); + + +static int factor1 = 0; +static int factor2 = 0; +static bool ready = false; + +static void factor1Handler(MicroBitEvent) +{ + factor1 = uBit.random(10); + uBit.display.print(factor1); + + ready = false; +} + +static void factor2Handler(MicroBitEvent) +{ + uBit.display.print(ImageMultiplier); + uBit.sleep(300); + + factor2 = uBit.random(10); + uBit.display.print(factor2); + + ready = true; +} + +static void helpHandler(MicroBitEvent) +{ + if (!ready) { + return; + } + + int result = factor1 * factor2; + uBit.display.scroll(result); + uBit.sleep(300); + + uBit.display.print(ImageArrowLeft); +} + +void multiplication_run() { + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + factor1Handler); + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + factor2Handler); + + uBit.messageBus.listen( + MICROBIT_ID_GESTURE, + MICROBIT_ACCELEROMETER_EVT_SHAKE, + helpHandler); + + uBit.display.print(ImageArrowLeft); + + leave = false; + while(!leave) { + uBit.sleep(100); + } + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + factor1Handler); + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + factor2Handler); + + uBit.messageBus.ignore( + MICROBIT_ID_GESTURE, + MICROBIT_ACCELEROMETER_EVT_SHAKE, + helpHandler); +} \ No newline at end of file diff --git a/source/RunMultiplication.h b/source/RunMultiplication.h new file mode 100644 index 0000000..9820f7d --- /dev/null +++ b/source/RunMultiplication.h @@ -0,0 +1,6 @@ +#ifndef RUN_MULTIPLICATION_H +#define RUN_MULTIPLICATION_H + +void multiplication_run(); + +#endif // RUN_MULTIPLICATION_H \ No newline at end of file diff --git a/source/RunOracle.cpp b/source/RunOracle.cpp new file mode 100644 index 0000000..a8c026e --- /dev/null +++ b/source/RunOracle.cpp @@ -0,0 +1,74 @@ +#include "RunOracle.h" +#include "MicroBit.h" +#include "Images.h" +#include "Utils.h" + +extern MicroBit uBit; + +void oracle_run() +{ + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + eventHandler); + + leave = false; + while(!leave) { + + event = false; + + leave = false; + while(!leave) { + + uBit.display.print(ImageArrowLeft); + + for(uint32_t t=0, i=10; t<400 && !leave && !event; t+=i) { + uBit.sleep(i); + } + + if (leave || event) { + break; + } + + uBit.sleep(10); + uBit.display.clear(); + + for(uint32_t t=0, i=10; t<400 && !leave && !event; t+=i) { + uBit.sleep(i); + } + } + + if (leave) { + break; + } + + for (int i = 0; i < 5; i++) { + uBit.display.print(ImageDot); + uBit.sleep(200); + uBit.display.clear(); + uBit.sleep(50); + } + + int r = uBit.random(100); + uBit.display.print((r < 50) + ? ImageSmiley + : ImageSadly + ); + + uBit.sleep(3000); + uBit.display.clear(); + } + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + eventHandler); +} \ No newline at end of file diff --git a/source/RunOracle.h b/source/RunOracle.h new file mode 100644 index 0000000..92f7dbb --- /dev/null +++ b/source/RunOracle.h @@ -0,0 +1,6 @@ +#ifndef RUN_ORACLE_H +#define RUN_ORACLE_H + +void oracle_run(); + +#endif // RUN_ORACLE_H \ No newline at end of file diff --git a/source/RunRockPaperScissors.cpp b/source/RunRockPaperScissors.cpp new file mode 100644 index 0000000..080eb05 --- /dev/null +++ b/source/RunRockPaperScissors.cpp @@ -0,0 +1,58 @@ +#include "RunRockPaperScissors.h" +#include "Images.h" +#include "MicroBit.h" +#include "Utils.h" + +extern MicroBit uBit; + +void rockpaperscissors_run() +{ + const uint16_t pause = 200; + + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + uBit.display.print(ImageRock, 0, 0, 0, pause * 2); + uBit.display.print(ImageFull, 0, 0, 0, pause * 2); + uBit.display.print(ImageScissors, 0, 0, 0, pause * 2); + uBit.display.print(ImageWell, 0, 0, 0, pause * 2); + uBit.display.clear(); + uBit.sleep(pause); + + leave = false; + while (!leave) { + + blinkImageUntilEvent( + MICROBIT_ID_GESTURE, + MICROBIT_ACCELEROMETER_EVT_SHAKE, + ImageDoubleRow, + -1, + 2, + 100); + + if (leave) { + break; + } + + int r = uBit.random(400); + if (r < 100) { + uBit.display.print(ImageRock); + } else if (r < 200) { + uBit.display.print(ImageFull); + } else if (r < 300) { + uBit.display.print(ImageScissors); + } else { + uBit.display.print(ImageWell); + } + + uBit.sleep(3000); + uBit.display.clear(); + } + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); +} \ No newline at end of file diff --git a/source/RunRockPaperScissors.h b/source/RunRockPaperScissors.h new file mode 100644 index 0000000..a2d147c --- /dev/null +++ b/source/RunRockPaperScissors.h @@ -0,0 +1,6 @@ +#ifndef RUN_ROCK_PAPER_SCISSORS_H +#define RUN_ROCK_PAPER_SCISSORS_H + +void rockpaperscissors_run(); + +#endif // RUN_ROCK_PAPER_SCISSORS_H \ No newline at end of file diff --git a/source/Snake.cpp b/source/RunSnake.cpp similarity index 56% rename from source/Snake.cpp rename to source/RunSnake.cpp index 0e332de..bf1568a 100644 --- a/source/Snake.cpp +++ b/source/RunSnake.cpp @@ -1,56 +1,31 @@ -/* -The MIT License (MIT) - -Adapted for Calliope by Matthias L. Jugel -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - +#include "RunSnake.h" #include "MicroBit.h" -#include "CalliopeDemo.h" +#include "Images.h" +#include "Utils.h" + +extern MicroBit uBit; #define SNAKE_EMPTY 0 -#define SNAKE_UP 1 -#define SNAKE_LEFT 2 +#define SNAKE_UP 1 +#define SNAKE_LEFT 2 #define SNAKE_RIGHT 3 -#define SNAKE_DOWN 4 +#define SNAKE_DOWN 4 -#define SNAKE_FRAME_DELAY 85 -#define GROWTH_SPEED 3 - -extern MicroBit uBit; -extern state_t state; -extern bool eventOK; +#define SNAKE_FRAME_DELAY 85 +#define GROWTH_SPEED 3 struct Point { int x; int y; }; -static Point head; // Location of the head of our snake. -static Point tail; // Location of the tail of our snake. -static Point food; // Location of food. +static Point head; +static Point tail; +static Point food; static MicroBitImage map(5, 5); -void place_food() { +static void placeFood() +{ int r = uBit.random(24); int x = 0; int y = 0; @@ -68,38 +43,42 @@ void place_food() { food.y = y; } +static void eatFood() +{ + uBit.rgb.setColour(0xff, 0x00, 0x00, 0x00); + uBit.soundmotor.soundOn(392); + fiber_sleep(500); + uBit.soundmotor.soundOn(440); + fiber_sleep(125); + uBit.soundmotor.soundOff(); + uBit.rgb.off(); +} + static bool accel_disabled = false; static bool a_pressed = false; static bool b_pressed = false; -void buttonAPressed(MicroBitEvent event) { - (void)event; +static void buttonAPressed(MicroBitEvent) +{ accel_disabled = true; a_pressed = true; } -void buttonBPressed(MicroBitEvent event) { - (void)event; +static void buttonBPressed(MicroBitEvent) +{ accel_disabled = true; b_pressed = true; } -void eatFood() { - uBit.rgb.setColour(0xff, 0x00, 0x00, 0x00); - uBit.soundmotor.soundOn(392); - fiber_sleep(500); - uBit.soundmotor.soundOn(440); - fiber_sleep(125); - uBit.soundmotor.soundOff(); - uBit.rgb.off(); -} +void snake_run() +{ + uBit.display.clear(); -void snake() { - Point newHead; // Calculated placement of new head position based on user input. - int hdirection = SNAKE_UP; // Head's direction of travel - int tdirection; // Tail's direction of travel - int snakeLength; // number of segments in the snake. - int growing; // boolean state indicating if we've just eaten some food. + Point newHead; // Calculated placement of new head position based on user input. + int hdirection = SNAKE_UP; // Head's direction of travel + int tdirection; // Tail's direction of travel + int snakeLength; // number of segments in the snake. + int growing; // boolean state indicating if we've just eaten some food. int score; int steps = 0; @@ -111,19 +90,30 @@ void snake() { score = 0; map.clear(); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, buttonAPressed); - uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, buttonBPressed); + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + buttonAPressed); + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + buttonBPressed); + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); uBit.display.image.setPixelValue(head.x, head.y, 255); - // Add some random food. - place_food(); + // Add some random food. + placeFood(); - while (state == Snake) { - // Flash the food is necessary; + leave = false; + while (!leave) { + // Flash the food is necessary; uBit.display.image.setPixelValue(food.x, food.y, uBit.systemTime() % 500 < 250 ? 0 : 255); - if(steps++ % 4 == 0) { + if (steps++ % 4 == 0) { newHead.x = head.x; newHead.y = head.y; @@ -132,41 +122,40 @@ void snake() { // switch direction according to buttons if (a_pressed || b_pressed) { switch (hdirection) { - case SNAKE_LEFT: - hdirection = (a_pressed ? SNAKE_DOWN : (b_pressed ? SNAKE_UP : SNAKE_LEFT)); - break; - case SNAKE_RIGHT: - hdirection = (a_pressed ? SNAKE_UP : (b_pressed ? SNAKE_DOWN : SNAKE_RIGHT)); - break; - case SNAKE_UP: - hdirection = (a_pressed ? SNAKE_LEFT : (b_pressed ? SNAKE_RIGHT : SNAKE_UP)); - break; - case SNAKE_DOWN: - hdirection = (a_pressed ? SNAKE_RIGHT : (b_pressed ? SNAKE_LEFT : SNAKE_DOWN)); - break; - default: - break; - } - } - - // do the actual move - switch (hdirection) { case SNAKE_LEFT: - newHead.x = newHead.x == 0 ? 4 : newHead.x - 1; + hdirection = (a_pressed ? SNAKE_DOWN : (b_pressed ? SNAKE_UP : SNAKE_LEFT)); break; case SNAKE_RIGHT: - newHead.x = newHead.x == 4 ? 0 : newHead.x + 1; + hdirection = (a_pressed ? SNAKE_UP : (b_pressed ? SNAKE_DOWN : SNAKE_RIGHT)); break; case SNAKE_UP: - newHead.y = newHead.y == 0 ? 4 : newHead.y - 1; + hdirection = (a_pressed ? SNAKE_LEFT : (b_pressed ? SNAKE_RIGHT : SNAKE_UP)); break; case SNAKE_DOWN: - newHead.y = newHead.y == 4 ? 0 : newHead.y + 1; + hdirection = (a_pressed ? SNAKE_RIGHT : (b_pressed ? SNAKE_LEFT : SNAKE_DOWN)); break; default: break; + } } + // do the actual move + switch (hdirection) { + case SNAKE_LEFT: + newHead.x = newHead.x == 0 ? 4 : newHead.x - 1; + break; + case SNAKE_RIGHT: + newHead.x = newHead.x == 4 ? 0 : newHead.x + 1; + break; + case SNAKE_UP: + newHead.y = newHead.y == 0 ? 4 : newHead.y - 1; + break; + case SNAKE_DOWN: + newHead.y = newHead.y == 4 ? 0 : newHead.y + 1; + break; + default: + break; + } } else { int dx = uBit.accelerometer.getX(); @@ -196,9 +185,8 @@ void snake() { int status = map.getPixelValue(newHead.x, newHead.y); if (status == SNAKE_UP || status == SNAKE_DOWN || status == SNAKE_LEFT || status == SNAKE_RIGHT) { - uBit.display.scroll(DISPLAY_THEEND); + uBit.display.scroll("The End"); uBit.display.scroll(score); - return; } @@ -243,7 +231,7 @@ void snake() { invoke(eatFood); growing++; score++; - place_food(); + placeFood(); } } @@ -251,7 +239,17 @@ void snake() { } uBit.display.clear(); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, buttonAPressed); - uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, buttonBPressed); -} + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_A, + MICROBIT_BUTTON_EVT_CLICK, + buttonAPressed); + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_B, + MICROBIT_BUTTON_EVT_CLICK, + buttonBPressed); + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); +} \ No newline at end of file diff --git a/source/RunSnake.h b/source/RunSnake.h new file mode 100644 index 0000000..e56695a --- /dev/null +++ b/source/RunSnake.h @@ -0,0 +1,6 @@ +#ifndef RUN_SNAKE_H +#define RUN_SNAKE_H + +void snake_run(); + +#endif // RUN_SNAKE_H \ No newline at end of file diff --git a/source/CalliopeTestBoard.cpp b/source/RunTests.cpp similarity index 71% rename from source/CalliopeTestBoard.cpp rename to source/RunTests.cpp index 91c88d1..a8cd072 100644 --- a/source/CalliopeTestBoard.cpp +++ b/source/RunTests.cpp @@ -1,91 +1,93 @@ -#include -#include "CalliopeDemo.h" +#include +#include "RunTests.h" +#include "MicroBit.h" +#include "MicroBitSerial.h" +#include "Images.h" +#include "Utils.h" extern MicroBit uBit; -void onButtonA(MicroBitEvent event) { - (void) event; +void onButtonA(MicroBitEvent) +{ uBit.display.print("A"); uBit.rgb.off(); uBit.rgb.setColour(0, 0, 255, 0); } -void onButtonB(MicroBitEvent event) { - (void) event; +void onButtonB(MicroBitEvent) +{ uBit.display.print("B"); uBit.rgb.off(); uBit.rgb.setColour(255, 0, 0, 0); - } -void onButtonAB(MicroBitEvent event) { - (void) event; +void onButtonAB(MicroBitEvent) +{ uBit.display.print("#"); uBit.rgb.off(); uBit.rgb.setColour(0, 255, 0, 0); } -void onButton0(MicroBitEvent event) { - (void) event; +void onButton0(MicroBitEvent) +{ uBit.display.print("0"); } -void onButton1(MicroBitEvent event) { - (void) event; +void onButton1(MicroBitEvent) +{ uBit.display.print("1"); } -void onButton2(MicroBitEvent event) { - (void) event; +void onButton2(MicroBitEvent) +{ uBit.display.print("2"); } -void onButton3(MicroBitEvent event) { - (void) event; +void onButton3(MicroBitEvent) +{ uBit.display.print("3"); } -void onShake(MicroBitEvent event) { - (void) event; +void onShake(MicroBitEvent) +{ uBit.display.print("S"); } -void onFaceUp(MicroBitEvent event) { - (void) event; +void onFaceUp(MicroBitEvent) +{ uBit.display.print("+"); } -void onFaceDown(MicroBitEvent event) { - (void) event; +void onFaceDown(MicroBitEvent) +{ uBit.display.print("-"); } -void onTiltUp(MicroBitEvent event) { - (void) event; +void onTiltUp(MicroBitEvent) +{ uBit.display.print("U"); } -void onTiltDown(MicroBitEvent event) { - (void) event; +void onTiltDown(MicroBitEvent) +{ uBit.display.print("D"); } -void onTiltLeft(MicroBitEvent event) { - (void) event; +void onTiltLeft(MicroBitEvent) +{ uBit.display.print("L"); } -void onTiltRight(MicroBitEvent event) { - (void) event; +void onTiltRight(MicroBitEvent) +{ uBit.display.print("R"); } -void testBoard() { - KeyValuePair *firstTime = uBit.storage.get("initial"); +void tests_run() +{ + uBit.serial.setTxBufferSize(MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE); - if (firstTime != NULL) return; - uint8_t done = 1; - uBit.storage.put("initial", &done, sizeof(int)); + uBit.sleep(200); uBit.serial.send("sound\r\n"); uBit.soundmotor.setSoundSilentMode(true); @@ -100,7 +102,7 @@ void testBoard() { uBit.serial.send("display\r\n"); uBit.display.clear(); - uBit.display.print(Full); + uBit.display.print(ImageFull); for (int i = 255; i > 0; i -= 2) { uBit.display.setBrightness(i); uBit.sleep(3); @@ -129,9 +131,8 @@ void testBoard() { uBit.accelerometer.getY(); uBit.accelerometer.getZ(); } - uBit.display.print(Tick); + uBit.display.print(ImageTick); - // we need to trigger touch sensing uBit.io.P12.isTouched(); uBit.io.P0.isTouched(); uBit.io.P1.isTouched(); @@ -153,14 +154,21 @@ void testBoard() { uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_TILT_LEFT, onTiltLeft); uBit.messageBus.listen(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT, onTiltRight); - while (1) { + uBit.sleep(500); + uBit.serial.send("microphone\r\n"); + uBit.display.clear(); + + while (true) { int mic = uBit.io.P21.getAnalogValue(); // ((value - fromLow) * (toHigh - toLow)) / (fromHigh - fromLow) + toLow if (mic > 512) { - const int gauge = ((log2(mic - 511) * 4) / 9); + const int gauge = static_cast((log2(mic - 511) * 4) / 9); - for (int i = 0; i <= 4; i++) { - uBit.display.image.setPixelValue(4, 4 - i, i > gauge ? 0 : 255); + for (uint8_t i = 0; i <= 4; i++) { + const int16_t x = static_cast(4); + const int16_t y = static_cast(4 - i); + const uint8_t t = static_cast(i > gauge ? 0 : 255); + uBit.display.image.setPixelValue(x, y, t); } uBit.sleep(10); diff --git a/source/RunTests.h b/source/RunTests.h new file mode 100644 index 0000000..9b41379 --- /dev/null +++ b/source/RunTests.h @@ -0,0 +1,6 @@ +#ifndef RUN_TESTS_H +#define RUN_TESTS_H + +void tests_run(); + +#endif // RUN_TESTS_H \ No newline at end of file diff --git a/source/RunVolumeMeter.cpp b/source/RunVolumeMeter.cpp new file mode 100644 index 0000000..af5abab --- /dev/null +++ b/source/RunVolumeMeter.cpp @@ -0,0 +1,86 @@ +#include +#include "RunVolumeMeter.h" +#include "MicroBit.h" +#include "Utils.h" + +extern MicroBit uBit; + +const int threshold = 519; + +static int read() +{ + int mic = 0; + const int n = 30; + for (uint8_t i = 0; i < n; i++) { + const int v = uBit.io.P21.getAnalogValue(); + if (v > mic) mic = v; + } + + if (mic < threshold) { + return 0; + } + + // mic 512...900 ~> 0...5 + const int gauge = static_cast((log2(mic - threshold + 1) * 5) / 8); + + // uBit.serial.printf("m:%d -> g:%d\n", mic, gauge); + + if (gauge < 0) { + return 0; + } + + if (gauge > 5) { + return 5; + } + + return gauge; +} + +void volumemeter_run() +{ + uBit.messageBus.listen( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); + + int gauges[5] = {}; + + leave = false; + while (!leave) { + + // shift values + for (uint8_t i = 0; i < (5-1); i++) { + gauges[i] = gauges[i+1]; + } + + gauges[4] = read(); + + for (uint16_t x = 0; x < 5; x++) { + const int gauge = gauges[x]; + for (uint16_t y = 0; y < 5; y++) { + const uint8_t t = static_cast(gauge > y ? 255 : 0); + uBit.display.image.setPixelValue(x, 4-y, t); + } + } + + int sum = 0; + for (uint8_t i = 0; i < 5; i++) { + sum += gauges[i]; + } + + if (sum > 5*2.5) { + uBit.rgb.setColour(0xFF, 0x00, 0x00, 0x00); + } else { + uBit.rgb.setColour(0x00, 0xFF, 0x00, 0x00); + } + + uBit.sleep(100); + } + + uBit.rgb.off(); + + uBit.messageBus.ignore( + MICROBIT_ID_BUTTON_AB, + MICROBIT_BUTTON_EVT_CLICK, + leaveHandler); +} \ No newline at end of file diff --git a/source/RunVolumeMeter.h b/source/RunVolumeMeter.h new file mode 100644 index 0000000..276e5fe --- /dev/null +++ b/source/RunVolumeMeter.h @@ -0,0 +1,6 @@ +#ifndef RUN_VOLUME_METER_H +#define RUN_VOLUME_METER_H + +void volumemeter_run(); + +#endif // RUN_VOLUME_METER_H \ No newline at end of file diff --git a/source/Slice.cpp b/source/Slice.cpp new file mode 100644 index 0000000..5069035 --- /dev/null +++ b/source/Slice.cpp @@ -0,0 +1,42 @@ +#include "Slice.h" + +Slice slice_create(uint8_t *buffer, uint16_t start, uint16_t stop) +{ + Slice slice = {}; + slice.buffer = buffer; + slice.start = start; + slice.position = start; + slice.stop = stop; + return slice; +} + +uint16_t slice_available(Slice &slice) +{ + if (slice.position < slice.start) { + return 0; + } + if (slice.position >= slice.stop) { + return 0; + } + return slice.stop - slice.position; +} + +uint8_t slice_read8(Slice &slice) +{ + if (slice.position < slice.start) { + // assert + return 0; + } + if (slice.position >= slice.stop) { + // assert + return 0; + } + return slice.buffer[slice.position++]; +} + +uint16_t slice_read16(Slice &slice) +{ + uint8_t hi = slice_read8(slice); + uint8_t lo = slice_read8(slice); + return hi << 8 | lo; +} diff --git a/source/Slice.h b/source/Slice.h new file mode 100644 index 0000000..2b2be62 --- /dev/null +++ b/source/Slice.h @@ -0,0 +1,18 @@ +#ifndef SLICE_H +#define SLICE_H + +#include + +typedef struct { + uint8_t *buffer; + uint16_t start; + uint16_t position; + uint16_t stop; +} Slice; + +Slice slice_create(uint8_t *buffer, uint16_t start, uint16_t stop); +uint16_t slice_available(Slice &slice); +uint8_t slice_read8(Slice &slice); +uint16_t slice_read16(Slice &slice); + +#endif // SLICE_H diff --git a/source/Storage.h b/source/Storage.h new file mode 100644 index 0000000..675a578 --- /dev/null +++ b/source/Storage.h @@ -0,0 +1,31 @@ +#ifndef STORAGE_H +#define STORAGE_H + +#define KEY_TEST "initial" +#define KEY_DEMO "demo" +#define KEY_INTERPRETER "interpreter" + +extern MicroBit uBit; + +inline bool hasStorageKey(const char* name) +{ + void* value = uBit.storage.get(name); + if (value != NULL) { + free(value); + return true; + } else { + return false; + } +} + +inline void setStorageKey(const char* name, uint8_t value = 1) +{ + uBit.storage.put(name, &value, sizeof(uint8_t)); +} + +inline void removeStorageKey(const char* name) +{ + uBit.storage.remove(name); +} + +#endif // STORAGE_H \ No newline at end of file diff --git a/source/Utils.cpp b/source/Utils.cpp new file mode 100644 index 0000000..a331d1f --- /dev/null +++ b/source/Utils.cpp @@ -0,0 +1,67 @@ +#include "Utils.h" +#include "MicroBit.h" +#include "MicroBitImage.h" + +extern MicroBit uBit; + +bool event = false; +void eventHandler(MicroBitEvent) +{ + event = true; +} + +bool leave = false; +void leaveHandler(MicroBitEvent) +{ + leave = true; +} + +void blinkImage( + const MicroBitImage& image, + const int pos, + const uint16_t delay) +{ + + event = false; + + uBit.display.print(image, pos); + + for (uint32_t t = 0, i = 10; t < delay && !leave && !event; t += i) { + uBit.sleep(i); + } + + if (event) { + return; + } + + uBit.display.clear(); + + for (uint32_t t = 0, i = 10; t < delay && !leave && !event; t += i) { + uBit.sleep(i); + } +} + +void blinkImageUntilEvent( + const uint16_t source, + const uint16_t value, + const MicroBitImage& image, + const int pos1, + const int pos2, + const uint16_t delay) +{ + uBit.messageBus.listen( + source, + value, + eventHandler); + + event = false; + + for (uint8_t i = 0; !leave && !event; i++) { + blinkImage(image, (i % 2) == 0 ? pos1 : pos2, delay); + } + + uBit.messageBus.ignore( + source, + value, + eventHandler); +} \ No newline at end of file diff --git a/source/Utils.h b/source/Utils.h new file mode 100644 index 0000000..e18c2c7 --- /dev/null +++ b/source/Utils.h @@ -0,0 +1,25 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "MicroBit.h" + +extern bool event; +void leaveHandler(MicroBitEvent); + +extern bool leave; +void eventHandler(MicroBitEvent); + +void blinkImage( + const MicroBitImage& image, + const int pos = 0, + const uint16_t delay = 200); + +void blinkImageUntilEvent( + const uint16_t source, + const uint16_t value, + const MicroBitImage& image, + const int pos1 = 0, + const int pos2 = 0, + const uint16_t delay = 200); + +#endif // UTILS_H \ No newline at end of file