diff --git a/examples/ci-tests/ble/Makefile b/examples/ci-tests/ble/Makefile new file mode 100644 index 000000000..35dae882c --- /dev/null +++ b/examples/ci-tests/ble/Makefile @@ -0,0 +1,17 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# External libraries used +EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/simple-ble + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk + +# Include simple-ble's Makefile so it's rebuilt automatically +include $(TOCK_USERLAND_BASE_DIR)/simple-ble/Makefile diff --git a/examples/ci-tests/ble/README.md b/examples/ci-tests/ble/README.md new file mode 100644 index 000000000..fd2e049a4 --- /dev/null +++ b/examples/ci-tests/ble/README.md @@ -0,0 +1,15 @@ +# Hardware CI Tests + +## Run Python Test + +To run the test, +```bash +sudo python3 test.py Nrf52840GpioTest +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test diff --git a/examples/ci-tests/ble/main.c b/examples/ci-tests/ble/main.c new file mode 100644 index 000000000..9b4b0a5d1 --- /dev/null +++ b/examples/ci-tests/ble/main.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +// Sizes in bytes +#define DEVICE_NAME_SIZE 6 +#define UUIDS_SIZE 4 +#define MANUFACTURER_DATA_SIZE 2 +#define FAKE_TEMPERATURE_DATA_SIZE 2 + +/******************************************************************************* + * MAIN + ******************************************************************************/ + +int main(void) { + int err; + printf("[Tutorial] BLE Advertising\n"); + + // declarations of variables to be used in this BLE example application + uint16_t advertising_interval_ms = 20; + uint8_t device_name[] = "TockOS"; + uint16_t uuids[] = {0x1800, 0x1809}; + uint8_t manufacturer_data[] = {0x13, 0x37}; + uint8_t fake_temperature_data[] = {0x00, 0x00}; + + static uint8_t adv_data_buf[ADV_DATA_MAX_SIZE]; + + // configure advertisement interval to 300ms + // configure LE only and discoverable + printf(" - Initializing BLE... %s\n", device_name); + AdvData_t adv_data = gap_adv_data_new(adv_data_buf, sizeof(adv_data_buf)); + + gap_add_flags(&adv_data, LE_GENERAL_DISCOVERABLE | BREDR_NOT_SUPPORTED); + + // configure device name as TockOS + printf(" - Setting the device name... %s\n", device_name); + err = gap_add_device_name(&adv_data, device_name, DEVICE_NAME_SIZE); + if (err < RETURNCODE_SUCCESS) + printf("ble_advertise_name, error: %s\r\n", tock_strrcode(err)); + + // configure list of UUIDs */ + printf(" - Setting the device UUID...\n"); + err = gap_add_service_uuid16(&adv_data, uuids, UUIDS_SIZE); + if (err < RETURNCODE_SUCCESS) + printf("ble_advertise_uuid16, error: %s\r\n", tock_strrcode(err)); + + // configure manufacturer data + printf(" - Setting manufacturer data...\n"); + err = gap_add_manufacturer_specific_data(&adv_data, manufacturer_data, + MANUFACTURER_DATA_SIZE); + if (err < RETURNCODE_SUCCESS) + printf("ble_advertise_manufacturer_specific_data, error: %s\r\n", + tock_strrcode(err)); + + // configure service data + printf(" - Setting service data...\n"); + err = gap_add_service_data(&adv_data, uuids[1], fake_temperature_data, + FAKE_TEMPERATURE_DATA_SIZE); + if (err < RETURNCODE_SUCCESS) + printf("ble_advertise_service_data, error: %s\r\n", tock_strrcode(err)); + + // start advertising + printf(" - Begin advertising! %s\n", device_name); + err = ble_start_advertising(ADV_NONCONN_IND, adv_data.buf, adv_data.offset, advertising_interval_ms); + if (err < RETURNCODE_SUCCESS) + printf("ble_start_advertising, error: %s\r\n", tock_strrcode(err)); + + // configuration complete + printf("Now advertising every %d ms as '%s'\n", advertising_interval_ms, + device_name); + return 0; +} diff --git a/examples/ci-tests/ble/test.py b/examples/ci-tests/ble/test.py new file mode 100644 index 000000000..8100a9c03 --- /dev/null +++ b/examples/ci-tests/ble/test.py @@ -0,0 +1,146 @@ +# BLE Test +# This tester corresponds to libtock-c/examples/ci-tests/ble test. + +import logging +import time +import unittest +import os +import subprocess + +TARGET_NAME = 'TockOS' + +################################################################################ +# Helper classes and functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('BLE Test') +logger.setLevel('INFO') + +logger.info('Initiating BLE test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class BleTest(unittest.TestCase): + def test_ble_advertise(self): + """Check if the advertised device name can be found""" + # Change line from docs to logging info + print() + + logger.info('Bluetooth Status on RPi Harness\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + os.system('sudo systemctl status bluetooth') + print() # Line change + os.system('sudo systemctl status hciuart') + print() # Line change + # os.system('sudo timeout 5 stdbuf -oL hcitool lescan') + # print() # Line change + scan_cmd = 'sudo timeout 5 stdbuf -oL hcitool lescan' + + # BLE scan flag + found = False + + try: + scan_result = subprocess.check_output(scan_cmd, + stderr=subprocess.STDOUT, + shell=True) + scan_result_str = scan_result.decode('ascii') + logger.info('Scan result:\n' + scan_result_str, + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Search for target board name + scan_entries = scan_result_str.split('\n') + print(scan_entries) + + for entry in scan_entries: + mac_addr, name = entry.split(' ') + print(name) + + if name == TARGET_NAME: + found = True + + except subprocess.CalledProcessError as err: + # Print error + scan_result_str = err.output.decode('ascii') + + logger.info('Scan result error:\n' + scan_result_str, + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Search for target board name + scan_entries = scan_result_str.split('\n') + + for entry in scan_entries: + if entry != '': + mac_addr, name = entry.split(' ', 1) + + if name == TARGET_NAME: + found = True + + finally: + logger.info('BLE scan ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Restart bluetooth + # Note: the scanning process is corrupted whenever we try to kill it, so + # for now, we resort to restarting bluetooth every test, but if + # there is a better implementation, feel free to change this. + logger.info('Restarting test harness bluetooth.', + extra={'timegap': time_gap(TEST_START_TIME)}) + os.system('sudo hciconfig hci0 down; sudo hciconfig hci0 up') + + self.assertTrue(found) + +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(BleTest): + def setUp(self): + logger.info('Setting up for nrf52840dk BLE test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +class HailTest(BleTest): + def setUp(self): + logger.info('Setting up for nrf52840dk BLE test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +class Nrf52Test(BleTest): + def setUp(self): + logger.info('Setting up for nrf52dk BLE test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/boot-up/README.md b/examples/ci-tests/boot-up/README.md new file mode 100644 index 000000000..0983f021e --- /dev/null +++ b/examples/ci-tests/boot-up/README.md @@ -0,0 +1,26 @@ +# On Boot Up Scripts + +## Run Python i2cboot + +**Important:** This script is used to solve the cold-start problem the raspberry pi has on boot when running as slave. Currently, after booting the pi, the first i2c master test on a board (tests that require the RPi to act as a slave) will have an empty buffer, causing test to fail. However, after the first test script for master rx/tx is executed, the tests will now pass and buffer is filled properly. To solve this issue, we run this script on boot for the RPi whenever logged in from ssh to run the initial transaction for testing scripts to pass when necessary and not be affected by this cold-start issue. + +`i2cboot.py` provides basic test that sends message from slave to master, and waits for master to send message containing the slave message. This is essentially a Rx/Tx test, but we are primarily focusing on the reception of master here. The message sent from master will be printed over a 4 second period. If message sent from master is the exact same from message sent from slave, the script will pass - but this does not matter (Expected to fail) + +To run the test, +```bash +sudo python3 i2cboot.py +``` + +Boards supported. + +Board | +------| +nrf52840dk | + +**NOTE** +This script requires the Raspberry Pi to be set as slave. Thus, you must use Broadcom Pins 10 & 11 (SDA & SCL resepectively) or GPIO pin 19 and 23. You must also download the open source python library "pigpio" which enables slave access on the Raspberry Pi. How to download the library is done here on **CI Hardware Documentation - Raspberry Pi setup** + +This script requires button activation on tested boards. Two jumpers are required for the activation of the reset button and user button on boards. Broadcom Pins on Raspberry PI used are pins 20 & 21 (GPIO Pins 38 & 40) which are used for user button and reset respectively. + +This test utilizes the pigpio open source library, but the function utilized, `bsc_i2c` , has a delay when updating the slave buffer being sent to master. This delay, to be specific, is that it takes an i2c transaction between master and slave to update the slave buffer properly. This is a limitation of the RPi slave configuration, not the board being tested i2c configuration. To combat this, we utilize a dummy transaction that runs a i2c transaction of sending messages for 4 seconds to master from slave, then reopens a transaction (the transacton being tested) with the updated slave buffer properly timed. For more information about this delay and what exactly is happening to handle it, check the code `i2cboot.py` and the function `dummy_transaction`. + diff --git a/examples/ci-tests/boot-up/i2cboot.py b/examples/ci-tests/boot-up/i2cboot.py new file mode 100644 index 000000000..773927078 --- /dev/null +++ b/examples/ci-tests/boot-up/i2cboot.py @@ -0,0 +1,291 @@ +# I2C Master Rx Test +# This tester corresponds to libtock-c/examples/ci-tests/i2c-master-rx test. + +from pathlib import Path +import time +import logging +import toml +from gpiozero import InputDevice +from gpiozero import OutputDevice +import os + +CONFIG_FILE = f'{Path.home()}/tock/tools/tock-test-harness/config.toml' +MESSAGE_SENT = "Hello I'm Slave" # Message Master sends to slave +MESSAGE_CONFIRMATION= '' +FIRST_RX = 0 +dummy = False #Setup for dummy transaction (Buffer takes one transaction session to update properly) + + +SDA = 10 # Broadcom pin 10 (P1 pin 19) +SCL = 11 # Broadcom pin 11 (P1 pin 23) +RESET = 21 # Broadcom pin 21 (P1 pin 40) +BUTTON_1 = 20 # Broadcom pin 20 (P1 pin 38) + +I2C_ADDR = 0x41 # Raspberry Pi Address + +button = OutputDevice(BUTTON_1) +reset_button = OutputDevice(RESET) + +# Set up PiGPIO properly by configuring it on pi then importing library +os.system('sudo pigpiod') +time.sleep(2) + +import pigpio +time.sleep(1) + +pi = pigpio.pi() # Configure the Raspberry Pi as slave +# PiGPIO configured. + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +def press_button(): + global BUTTON_1 + """Button is one of User Buttons""" + + button.on() + time.sleep(1.5) + button.off() + +def reset(): + global RESET + + reset_button.on() + time.sleep(1.1) + reset_button.off() + reset_button.toggle() # Set Pin to tri-state + +def i2c(id, tick): + global pi + global FIRST_RX + global MESSAGE_CONFIRMATION + global I2C_ADDR + global dummy + + s, b, d = pi.bsc_i2c(I2C_ADDR, b"\nHello I'm Slave\n") + + # Check if dummy transaction is occuring, if not, test has started. + if dummy: + if b: + if(FIRST_RX < 1): + MESSAGE_CONFIRMATION = d.decode() + FIRST_RX += 1 + + array = str(d[:-1]) + logger.info('Messsage Call Back From Master: ' + array, + extra={'timegap': time_gap(TEST_START_TIME)}) + +def dummy_transaction(): + global pi + global I2C_ADDR + global dummy + """ + This function is used to conteract the update delay + on the i2c slave buffer. The delay occurs when the buffer is + updated and requires the bus to enact a transcation before the + update to the buffer actually takes place. This function, then, + initiates that transaction to occur, and update the buffer in proper time. + """ + + if not pi.connected: + exit() + + press_button() + + # Add pull-ups in case external pull-ups haven't been added (For Raspberry Pi) + + pi.set_pull_up_down(SDA, pigpio.PUD_UP) + pi.set_pull_up_down(SCL, pigpio.PUD_UP) + + # Respond to BSC slave activity + e = pi.event_callback(pigpio.EVENT_BSC, i2c) + + pi.bsc_i2c(I2C_ADDR) + + time.sleep(3) + + # Reset Operation + time.sleep(1) + reset() + time.sleep(1) + close() + +def close(): + time.sleep(5) + + pi.stop() + os.system('sudo killall pigpiod') + + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('I2C Master Rx Boot Test') +logger.setLevel('INFO') + + +# END + +################################################################################ +# Test Case Module +################################################################################ + + +def main(): + """ Set up for Raspberry Pi configuration """ + global pi + global MESSAGE_CONFIRMATION + global dummy + + runTest = "" + with open(CONFIG_FILE, 'r') as config_toml: + config = toml.load(config_toml) + runTest = config['env']['i2c_on_boot'] + + if runTest == 'yes': + logger.info('Initiating I2C Master Rx Boot Test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + dummy_transaction() # Initiate the dummy transaction to update buffer in proper time + dummy = True # Update to initiate the dummy transaction properly on the i2c function + + # Set up pi gpio daemon again to clear buffer and reset transaction + time.sleep(1) + os.system('sudo pigpiod') + time.sleep(1) + + pi = pigpio.pi() + ################################################################### + + print() + logger.info('Sending Messages As Slave... ', + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = False + + if not pi.connected: + exit() + + time.sleep(2) # Wait until app has fully reset + + press_button() # Used to press one of the user buttons on the board + + # Add pull-ups in case external pull-ups haven't been added (For Raspberry Pi) + + pi.set_pull_up_down(SDA, pigpio.PUD_UP) + pi.set_pull_up_down(SCL, pigpio.PUD_UP) + + # Respond to BSC slave activity + e = pi.event_callback(pigpio.EVENT_BSC, i2c) + + pi.bsc_i2c(I2C_ADDR) # Configure BSC as I2C slave + + print() + logger.info('Initiating Transmission of Messages...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(4) # Time to wait for messages to be sent (Should see four messages logged) + + MESSAGE_CONFIRMATION = str(MESSAGE_CONFIRMATION) + MESSAGE_CONFIRMATION = MESSAGE_CONFIRMATION.strip() + + if (MESSAGE_CONFIRMATION == MESSAGE_SENT): + + # Close setup + close() + + logger.info('Message Sent: ' + MESSAGE_SENT, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Called Back from Master: ' + MESSAGE_CONFIRMATION, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection Satisfied. (Not Expected - But Fine to continue)', + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = True + + time.sleep(1) + + logger.info('I2C Master Rx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + else: + + # Close setup + + close() + + logger.info('Message Sent: ' + MESSAGE_SENT, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Called Back from Master: ' + MESSAGE_CONFIRMATION, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection was not Satisfied. Wrong/No message was sent. (Expected)', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + logger.info('I2C Master Rx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + else: + logger.info("Not Running I2C Boot Test...", + extra={'timegap': time_gap(TEST_START_TIME)}) + + #Closing App + close() + reset() +# END + +################################################################################ +# Test Case Setup +################################################################################ + +def startup(): + + time.sleep(1) + reset() # Used to activate the reset button on board + time.sleep(1) + + logger.info('Setting up for nrf52840dk I2C Master Rx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + main() + + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + main() diff --git a/examples/ci-tests/gpio/Makefile b/examples/ci-tests/gpio/Makefile new file mode 100644 index 000000000..54d6a7969 --- /dev/null +++ b/examples/ci-tests/gpio/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/ci-tests/gpio/README.md b/examples/ci-tests/gpio/README.md new file mode 100644 index 000000000..2e931f28b --- /dev/null +++ b/examples/ci-tests/gpio/README.md @@ -0,0 +1,20 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic tests that polls the pin output every 500 +milliseconds, and it monitors whether a toggle of the pin has occurred. If it +has occurred, it records the period to toggle the pin. The expected period +should be around 1 seconds with a margin of error within 1 millisecond. + +To run the test, +```bash +python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test diff --git a/examples/ci-tests/gpio/main.c b/examples/ci-tests/gpio/main.c new file mode 100644 index 000000000..6573247b0 --- /dev/null +++ b/examples/ci-tests/gpio/main.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* The gpio pin gets toggled every 1000 ms */ +#define TOGGLE_PERIOD 1000 + +/* The number of toggle for each pin */ +#define TOGGLE_COUNT 4 + +/* Amount of cycle until timeout is claimed */ +#define TIMEOUT 1 + +/* Maximum GPIO pin number */ +#define MAX_PIN_NO 15 + +/* Prototype */ +uint8_t test_pin(bool *); +void set_gpios(void); + + +int callback_count = 0; +// callback for timers +static void timer_cb (__attribute__ ((unused)) int arg0, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + callback_count = callback_count + 1; + *((bool*)userdata) = 1; +} + +void set_gpios(void) { + GPIO_Pin_t i = 0; + + for (; i <= MAX_PIN_NO; i++) { + gpio_enable_output(i); + } + + /* Set pin to high */ + for (i = 0; i <= MAX_PIN_NO; i++) { + gpio_set(i); + } +} + +uint8_t test_pin(bool * int_flag) { + uint8_t attempt_count = 0; + + while (attempt_count < TIMEOUT) { + + for (uint8_t i = 0; i < TOGGLE_COUNT; i++) { + yield_for(int_flag); + *int_flag = 0; /* Reset indicator */ + for (GPIO_Pin_t j = 0; j <= MAX_PIN_NO; j++) { + gpio_toggle(j); + } + } + + attempt_count++; + } + + return 1; +} + +static void gpio_test(void) { + tock_timer_t timer; /* Timer object */ + static bool int_flag = 0; /* Toggle trigger */ + + /* Initiate repeating timer */ + timer_every(TOGGLE_PERIOD, timer_cb, &int_flag, &timer); + + set_gpios(); + + /* Main loop */ + while (1) { + test_pin(&int_flag); + } +} + +int main(void) { + gpio_test(); + return 0; +} diff --git a/examples/ci-tests/gpio/test.py b/examples/ci-tests/gpio/test.py new file mode 100644 index 000000000..0295ac51d --- /dev/null +++ b/examples/ci-tests/gpio/test.py @@ -0,0 +1,205 @@ +# GPIO Test +# This tester corresponds to libtock-c/examples/ci-tests/gpio test. + +import logging +import time +import unittest +from gpiozero import InputDevice + +################################################################################ +# Helper classes and functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +class Pin: + """Pin object""" + def __init__(self, pin_no, pin_name = None): + # Set initial variables to invalid value + self.pin = InputDevice(pin_no) + + # Monitor variable + self.prev_state = -1 + self.prev_toggle_time = -1 + self.toggle_period = -1 + + # Test variable + self.has_toggled = False + + def read_pin(self): + """Reads input state and record toggle if a toggle of pin has been + observed. + + Argument: + state (boolean) - Input state of pin + """ + + # Check current pin state + if self.pin.value: + if self.prev_state == 0: + # Toggle appeared + if self.prev_toggle_time > 0: + self.toggle_period = time.time() - self.prev_toggle_time + self.has_toggled = True + + self.prev_toggle_time = time.time() + + self.prev_state = 1 + + else: + if self.prev_state == 1: + # Toggle appeared + if self.prev_toggle_time > 0: + self.toggle_period = time.time() - self.prev_toggle_time + self.has_toggled = True + + self.prev_toggle_time = time.time() + + self.prev_state = 0 + + def reset(self): + """Reset will only reset value of toggle_period by design""" + self.toggle_period = -1 + + def is_toggled(self): + """Check if the pin has been toggled in the past cycle""" + return self.toggle_period > 0 + +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('GPIO Test') +logger.setLevel('INFO') + +logger.info('Initiating GPIO test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class GpioTest(unittest.TestCase): + def within_margin_error(self, value, target, margin_err): + """Check if the value is within the given margin of error""" + return value < target + margin_err and value > target - margin_err + + # Since the test toggles pin 0 every 1 second, the tester shall poll the pin + # state twice the output rate to capture the changes. + def test_sync_toggle(self): + """Monitoring pin state every 500 ms + + Note: Since the test toggles pin every 1 second, the tester shall poll + the pin state twice the output rate to capture the changes. + """ + # Change line from docs to logging info + print() + + TEST_CYCLE_TIME = 8 + EXPECTED_TIME_PERIOD = 1 + MOE = 0.01 + MAX_PIN_NO = 5 + + for pin_no in range(0, MAX_PIN_NO): + # If pin exists + if hasattr(self, f'P{pin_no}'): + pin_wrapper = getattr(self, f'P{pin_no}') + + # Monitor pin over cycle count + for n in range(TEST_CYCLE_TIME): + + # Log pin state + logger.info( + (f'P{pin_no} state: high' + if pin_wrapper.pin.value + else f'P{pin_no} state: low'), + extra={'timegap': time_gap(TEST_START_TIME)}) + + pin_wrapper.read_pin() + + # If pin just toggled + if pin_wrapper.is_toggled(): + logger.info( + (f'P{pin_no} state toggled in ' + + f'{pin_wrapper.toggle_period}s'), + extra={'timegap': time_gap(TEST_START_TIME)}) + + # ASSERT toggle period around 1 second + self.assertTrue( + self.within_margin_error(pin_wrapper.toggle_period, + EXPECTED_TIME_PERIOD, + MOE)) + + pin_wrapper.reset() + + # End of cycle, go to sleep + time.sleep(0.5) + + # Ensure pin has toggled + self.assertTrue(pin_wrapper.has_toggled) + +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(GpioTest): + def setUp(self): + logger.info('Setting up for nrf52840dk GPIO test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Assign input pins + self.P0 = Pin(5) + self.P1 = Pin(6) + self.P2 = Pin(13) + self.P3 = Pin(19) + self.P4 = Pin(26) + +class HailTest(GpioTest): + def setUp(self): + logger.info('Setting up for Hail GPIO test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Assign input pins + self.P0 = Pin(5) + self.P1 = Pin(6) + self.P2 = Pin(13) + self.P3 = Pin(19) + +class Nrf52Test(GpioTest): + def setUp(self): + logger.info('Setting up for nrf52dk GPIO test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Assign input pins + self.P0 = Pin(5) + self.P1 = Pin(6) + self.P2 = Pin(13) + self.P3 = Pin(19) +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/i2c-master-rx/Makefile b/examples/ci-tests/i2c-master-rx/Makefile new file mode 100644 index 000000000..a38de484d --- /dev/null +++ b/examples/ci-tests/i2c-master-rx/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/ci-tests/i2c-master-rx/README.md b/examples/ci-tests/i2c-master-rx/README.md new file mode 100644 index 000000000..79f526091 --- /dev/null +++ b/examples/ci-tests/i2c-master-rx/README.md @@ -0,0 +1,26 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic test that sends message from slave to master, and waits for master to send message containing the slave message. This is essentially a Rx/Tx test, but we are primarily focusing on the reception of master here. The message sent from master will be printed over a 12 second period. If message sent from master is the exact same from message sent from slave, the test will pass. + +To run the test, +```bash +sudo python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test + +**NOTE** +This test requires the Raspberry Pi to be set as slave. Thus, you must use Broadcom Pins 10 & 11 (SDA & SCL resepectively) or GPIO pin 19 and 23. You must also download the open source python library "pigpio" which enables slave access on the Raspberry Pi. How to download the library is done here on **CI Hardware Documentation - Raspberry Pi setup** + +This test requires button activation on tested boards. Two jumpers are required for the activation of the reset button and user button on boards. Broadcom Pins on Raspberry PI used are pins 20 & 21 (GPIO Pins 38 & 40) which are used for user button and reset respectively. + +This test utilizes the pigpio open source library, but the function utilized, `bsc_i2c` , has a delay when updating the slave buffer being sent to master. This delay, to be specific, is that it takes an i2c transaction between master and slave to update the slave buffer properly. This is a limitation of the RPi slave configuration, not the board being tested i2c configuration. To combat this, we utilize a dummy transaction that runs a i2c transaction of sending messages for 4 seconds to master from slave, then reopens a transaction (the transacton being tested) with the updated slave buffer properly timed. For more information about this delay and what exactly is happening to handle it, check the code `test.py` and the function `dummy_transaction`. + +After Raspberry Pi initial boot, test will fail to receieve a message from device-to-test, due to pigpio daemon not booting properly. Run test again for better measurment after booting RPi. diff --git a/examples/ci-tests/i2c-master-rx/main.c b/examples/ci-tests/i2c-master-rx/main.c new file mode 100644 index 000000000..bbdb8f3e6 --- /dev/null +++ b/examples/ci-tests/i2c-master-rx/main.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; + +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_READ) { + printf("Master Reader\n"); + delay_ms(2500); + + printf("Read Buffer After: >%.*s<\n", BUF_SIZE, master_read_buf); + memcpy((char*) master_write_buf, (char*) master_read_buf, BUF_SIZE); + //master_write_buf[BUF_SIZE - 1] = '\0'; + printf("Write Buf Now: >%.*s<\n", BUF_SIZE, master_write_buf); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + delay_ms(1500); + + printf("Sending; >%.*s<\n", BUF_SIZE, master_write_buf); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Getting Message\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_read(FOLLOW_ADDRESS, BUF_SIZE)); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master Read Test\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + strcpy((char*) master_read_buf, "Hello friend.\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(LEADER_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + printf("Read Buffer Before: >%.*s<\n", BUF_SIZE, master_read_buf); + + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} diff --git a/examples/ci-tests/i2c-master-rx/test.py b/examples/ci-tests/i2c-master-rx/test.py new file mode 100644 index 000000000..4536fe30f --- /dev/null +++ b/examples/ci-tests/i2c-master-rx/test.py @@ -0,0 +1,279 @@ +# I2C Master Rx Test +# This tester corresponds to libtock-c/examples/ci-tests/i2c-master-rx test. + +import time +import logging +import unittest +from gpiozero import InputDevice +from gpiozero import OutputDevice +import os + +MESSAGE_SENT = "Hello I'm Slave" # Message Master sends to slave +MESSAGE_CONFIRMATION= '' +FIRST_RX = 0 +dummy = False #Setup for dummy transaction (Buffer takes one transaction session to update properly) + + +SDA = 10 # Broadcom pin 10 (P1 pin 19) +SCL = 11 # Broadcom pin 11 (P1 pin 23) +RESET = 21 # Broadcom pin 21 (P1 pin 40) +BUTTON_1 = 20 # Broadcom pin 20 (P1 pin 38) + +I2C_ADDR = 0x41 # Raspberry Pi Address + +button = OutputDevice(BUTTON_1) +reset_button = OutputDevice(RESET) + +# Set up PiGPIO properly by configuring it on pi then importing library +os.system('sudo pigpiod') +time.sleep(2) + +import pigpio +time.sleep(1) + +pi = pigpio.pi() # Configure the Raspberry Pi as slave +# PiGPIO configured. + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +def press_button(): + global BUTTON_1 + """Button is one of User Buttons""" + + button.on() + time.sleep(1.5) + button.off() + +def reset(): + global RESET + + reset_button.on() + time.sleep(1.1) + reset_button.off() + reset_button.toggle() # Set Pin to tri-state + +def i2c(id, tick): + global pi + global FIRST_RX + global MESSAGE_CONFIRMATION + global I2C_ADDR + global dummy + + s, b, d = pi.bsc_i2c(I2C_ADDR, b"\nHello I'm Slave\n") + + # Check if dummy transaction is occuring, if not, test has started. + if dummy: + if b: + if(FIRST_RX < 1): + MESSAGE_CONFIRMATION = d.decode() + FIRST_RX += 1 + + array = str(d[:-1]) + logger.info('Messsage Call Back From Master: ' + array, + extra={'timegap': time_gap(TEST_START_TIME)}) + +def dummy_transaction(): + global pi + global I2C_ADDR + global dummy + """ + This function is used to conteract the update delay + on the i2c slave buffer. The delay occurs when the buffer is + updated and requires the bus to enact a transcation before the + update to the buffer actually takes place. This function, then, + initiates that transaction to occur, and update the buffer in proper time. + """ + + if not pi.connected: + exit() + + press_button() + + # Add pull-ups in case external pull-ups haven't been added (For Raspberry Pi) + + pi.set_pull_up_down(SDA, pigpio.PUD_UP) + pi.set_pull_up_down(SCL, pigpio.PUD_UP) + + # Respond to BSC slave activity + e = pi.event_callback(pigpio.EVENT_BSC, i2c) + + pi.bsc_i2c(I2C_ADDR) + + time.sleep(4) + + # Reset Operation + time.sleep(1) + reset() + time.sleep(1) + close() + +def close(): + time.sleep(5) + + pi.stop() + os.system('sudo killall pigpiod') + + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('I2C Master Rx Test') +logger.setLevel('INFO') + +logger.info('Initiating I2C Master Rx Test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class I2CMasterRxTest(unittest.TestCase): + def test_i2c_master_rx_configuration(self): + """ Set up for Raspberry Pi configuration """ + global pi + global MESSAGE_CONFIRMATION + global dummy + + + dummy_transaction() # Initiate the dummy transaction to update buffer in proper time + dummy = True # Update to initiate the dummy transaction properly on the i2c function + + # Set up pi gpio daemon again to clear buffer and reset transaction + time.sleep(1) + os.system('sudo pigpiod') + time.sleep(1) + + pi = pigpio.pi() + ################################################################### + + print() + logger.info('Sending Messages As Slave... ', + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = False + + if not pi.connected: + exit() + + time.sleep(2) # Wait until app has fully reset + + press_button() # Used to press one of the user buttons on the board + + # Add pull-ups in case external pull-ups haven't been added (For Raspberry Pi) + + pi.set_pull_up_down(SDA, pigpio.PUD_UP) + pi.set_pull_up_down(SCL, pigpio.PUD_UP) + + # Respond to BSC slave activity + e = pi.event_callback(pigpio.EVENT_BSC, i2c) + + pi.bsc_i2c(I2C_ADDR) # Configure BSC as I2C slave + + print() + logger.info('Initiating Transmission of Messages...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(12) # Time to wait for messages to be sent (Should see four messages logged) + + MESSAGE_CONFIRMATION = str(MESSAGE_CONFIRMATION) + MESSAGE_CONFIRMATION = MESSAGE_CONFIRMATION.strip() + + if (MESSAGE_CONFIRMATION == MESSAGE_SENT): + + # Close setup + close() + + logger.info('Message Sent: ' + MESSAGE_SENT, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Called Back from Master: ' + MESSAGE_CONFIRMATION, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection Satisfied.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = True + + time.sleep(1) + + logger.info('I2C Master Rx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + self.assertTrue(received) + + else: + + # Close setup + + close() + + logger.info('Message Sent: ' + MESSAGE_SENT, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Called Back from Master: ' + MESSAGE_CONFIRMATION, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection was not Satisfied. Wrong/No message was sent.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + logger.info('I2C Master Rx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + self.assertTrue(received) + + +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(I2CMasterRxTest): + def setUp(self): + + time.sleep(1) + reset() # Used to activate the reset button on board + time.sleep(1) + + logger.info('Setting up for nrf52840dk I2C Master Rx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/i2c-master-tx/Makefile b/examples/ci-tests/i2c-master-tx/Makefile new file mode 100644 index 000000000..a38de484d --- /dev/null +++ b/examples/ci-tests/i2c-master-tx/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/ci-tests/i2c-master-tx/README.md b/examples/ci-tests/i2c-master-tx/README.md new file mode 100644 index 000000000..143fc704d --- /dev/null +++ b/examples/ci-tests/i2c-master-tx/README.md @@ -0,0 +1,24 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic test that sends message from master to slave, and waits for a 12 second period of master sending messages, but captures the first message sent. The message sent from master will be printed over a 12 second period. If message sent from master is the same as the expected message to be sent, then test will pass. + +To run the test, +```bash +sudo python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test + +**NOTE** +This test requires the Raspberry Pi to be set as slave. Thus, you must use Broadcom Pins 10 & 11 (SDA & SCL resepectively) or GPIO pin 19 and 23. You must also download the open source python library "pigpio" which enables slave access on the Raspberry Pi. How to download the library is done here on **CI Hardware Documentation - Raspberry Pi setup** + +This test requires button activation on tested boards. Two jumpers are required for the activation of the reset button and user button on boards. Broadcom Pins on Raspberry PI used are pins 20 & 21 (GPIO Pins 38 & 40) which are used for user button and reset respectively. The user button and reset button depend on board tested. + +After Raspberry Pi initial Boot, test will always fail with a null character message being sent. Run test again for a more accurate measurement. diff --git a/examples/ci-tests/i2c-master-tx/main.c b/examples/ci-tests/i2c-master-tx/main.c new file mode 100644 index 000000000..b4bd5a571 --- /dev/null +++ b/examples/ci-tests/i2c-master-tx/main.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + printf("CB: Master write\n"); + + delay_ms(2500); + + printf("Sending: Hello friend.\n"); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Switching to master\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + printf("Switched\n"); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master Write App!\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(LEADER_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} diff --git a/examples/ci-tests/i2c-master-tx/test.py b/examples/ci-tests/i2c-master-tx/test.py new file mode 100644 index 000000000..9af4c9ca7 --- /dev/null +++ b/examples/ci-tests/i2c-master-tx/test.py @@ -0,0 +1,233 @@ +# I2C Master Tx Test +# This tester corresponds to libtock-c/examples/ci-tests/i2c-master-tx test. + +import time +import logging +import unittest +import RPi.GPIO as GPIO +import os +from gpiozero import InputDevice +from gpiozero import OutputDevice + +MESSAGE = 'Hello friend.' # Message Master sends to slave +MESSAGE_RECEIVED = '' +FIRST_RX = 0 + +SDA = 10 # Broadcom pin 10 (P1 pin 19) +SCL = 11 # Broadcom pin 11 (P1 pin 23) +RESET = 21 # Broadcom pin 21 (P1 pin 40) +BUTTON_1 = 20 # Broadcom pin 20 (P1 pin 38) + +I2C_ADDR = 0x41 # Raspberry Pi Address + +button = OutputDevice(BUTTON_1) +reset_button = OutputDevice(RESET) + +# Set up PiGPIO properly by configuring it on pi then importing library +os.system('sudo pigpiod') +time.sleep(1) +import pigpio + +pi = pigpio.pi() # Configure the Raspberry Pi as slave +# PiGPIO configured. + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +def press_button(): + global BUTTON_1 + """Button is one of User Buttons""" + + button.on() + time.sleep(1.5) + button.off() + +def reset(): + global RESET + """Button used to reset the app""" + + reset_button.on() + time.sleep(1.1) + reset_button.off() + reset_button.toggle() # Set Pin to tri-state + +def i2c(id, tick): + global pi + global FIRST_RX + global MESSAGE_RECEIVED + global I2C_ADDR + + s, b, d = pi.bsc_i2c(I2C_ADDR) + + if b: + array = str(d[:-1]) + logger.info('Messsage Received: ' + array, + extra={'timegap': time_gap(TEST_START_TIME)}) + + if(FIRST_RX < 1): + string_received = d.decode() + string_split = string_received.splitlines() + MESSAGE_RECEIVED = string_split[0] + FIRST_RX += 1 + +def close(): + time.sleep(5) + + pi.stop() + os.system('sudo killall pigpiod') + +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('I2C Master Tx Test') +logger.setLevel('INFO') + +logger.info('Initiating I2C Master Tx Test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class I2CMasterTxTest(unittest.TestCase): + def test_i2c_master_tx_configuration(self): + """ Set up for Raspberry Pi configuration """ + global pi + global MESSAGE_RECEIVED + + print() + logger.info('Receiving Message As Slave... ', + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = False + + if not pi.connected: + exit() + + time.sleep(2) # Wait until app has fully reset + + press_button() # Used to press one of the user buttons on the board + + # Add pull-ups in case external pull-ups haven't been added (For Raspberry Pi) + + pi.set_pull_up_down(SDA, pigpio.PUD_UP) + pi.set_pull_up_down(SCL, pigpio.PUD_UP) + + # Respond to BSC slave activity + e = pi.event_callback(pigpio.EVENT_BSC, i2c) + + pi.bsc_i2c(I2C_ADDR) # Configure BSC as I2C slave + + print() + logger.info('Initiating Reception of Messages...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(12) # Time to wait for messages to be sent (Should see four messages logged) + + MESSAGE_RECEIVED = str(MESSAGE_RECEIVED) + MESSAGE_RECEIVED = MESSAGE_RECEIVED.strip() + + if (MESSAGE_RECEIVED == MESSAGE): + + # Close setup + close() + + logger.info('Expected Message: ' + MESSAGE, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Received: ' + MESSAGE_RECEIVED, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection Satisfied.', + extra={'timegap': time_gap(TEST_START_TIME)}) + received = True + + time.sleep(1) + + logger.info('I2C Master Tx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + os.system('sudo killall pigpiod') + time.sleep(1) + + self.assertTrue(received) + + else: + + # Close setup + close() + + logger.info('Expected Message: ' + MESSAGE, + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Message Received: ' + MESSAGE_RECEIVED, + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + time.sleep(1) + + logger.info('Connection was not Satisfied. Wrong/No message received.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + logger.info('I2C Master Tx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + os.system('sudo killall pigpiod') + time.sleep(1) + + self.assertTrue(received) + + +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(I2CMasterTxTest): + def setUp(self): + + time.sleep(1) + reset() # Used to activate the reset button on board + time.sleep(1) + + logger.info('Setting up for nrf52840dk I2C Master Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/i2c-slave-rx/Makefile b/examples/ci-tests/i2c-slave-rx/Makefile new file mode 100644 index 000000000..a38de484d --- /dev/null +++ b/examples/ci-tests/i2c-slave-rx/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/ci-tests/i2c-slave-rx/README.md b/examples/ci-tests/i2c-slave-rx/README.md new file mode 100644 index 000000000..1df866752 --- /dev/null +++ b/examples/ci-tests/i2c-slave-rx/README.md @@ -0,0 +1,20 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic test that sends message from master to slave. Tests if slave receives message through proper connection with specific function. If function fails, and throws out exception, then test fails. Otherwise, test will pass if connection is satisfied. + +To run the test, +```bash +sudo python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test + +**NOTE** +This test requires the Raspberry Pi to be set as Master. Thus, you must use Broadcom Pins 2 & 3 (SDA & SCL resepectively) or GPIO pin 3 and 5. \ No newline at end of file diff --git a/examples/ci-tests/i2c-slave-rx/main.c b/examples/ci-tests/i2c-slave-rx/main.c new file mode 100644 index 000000000..45bca594e --- /dev/null +++ b/examples/ci-tests/i2c-slave-rx/main.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define BUF_SIZE 17 +#define FOLLOW_ADDRESS 0x41 + +uint8_t slave_write_buf[BUF_SIZE]; +uint8_t slave_read_buf[BUF_SIZE]; + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + + if (callback_type == TOCK_I2C_CB_SLAVE_WRITE) { + printf("CB: Slave write\n"); + printf("Write Buffer v2 is >%.*s<\n", BUF_SIZE, slave_write_buf); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +int main(void) { + printf("I2C Slave Read \n"); + + //Preparing buffer + strncpy((char*) slave_write_buf, "0123456789ABCDEF", BUF_SIZE); + printf("Buffer is >%.*s<\n", BUF_SIZE, slave_write_buf); + + // Set up I2C peripheral + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_write_buffer(slave_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_read_buffer(slave_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(FOLLOW_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + +} diff --git a/examples/ci-tests/i2c-slave-rx/test.py b/examples/ci-tests/i2c-slave-rx/test.py new file mode 100644 index 000000000..ee81aa2b4 --- /dev/null +++ b/examples/ci-tests/i2c-slave-rx/test.py @@ -0,0 +1,181 @@ +# I2C Slave Rx Test +# This tester corresponds to libtock-c/examples/ci-tests/i2c-slave-rx test. + +from smbus import SMBus +import time +import logging +import unittest +from gpiozero import InputDevice +from gpiozero import OutputDevice + +ADDRESS = 0x41 # bus address to the Slave Device +MASTER = 0x40 # Raspberry Pi Master Address +SDA = 10 # Broadcom pin 10 (P1 pin 19) +SCL = 11 # Broadcom pin 11 (P1 pin 23) +MESSAGE = "Hello I'm Master" # Message to send to slave +bus = SMBus(1) # indicates /dev/ic2-1 + +pin_1 = InputDevice(10) +pin_2 = InputDevice(11) + +RESET = 21 # Broadcom pin 21 (P1 pin 40) + +reset_button = OutputDevice(RESET) + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +def reset(): + global RESET + """Button is Reset""" + + reset_button.off() + time.sleep(1.1) + reset_button.on() + time.sleep(0.5) + +def message_converter(message): + """Return list of ascii values for each character in message + Argument: + string I2C_message + """ + encoded = [] # Encoded message for ascii values + chars = list(message) # Spliting the string message into characters + + for items in chars: + encoded.append(ord(items)) # Filling encoded with ascii values of characters + + return encoded + +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('I2C Rx Test') +logger.setLevel('INFO') + +logger.info('Initiating I2C Rx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class I2CSlaveRxTest(unittest.TestCase): + def test_i2c_slave_rx_configuration(self): +# gpio_reset() # Reset gpio output of pins +# pi_daemon_reset() + + print() + logger.info('Sending I2C Message: ' + MESSAGE, + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = False + + message_to_send = message_converter(MESSAGE) + + time.sleep(1) + try: + bus.write_i2c_block_data(ADDRESS, MASTER, message_to_send) + + logger.info('Message Sent Sucessfully.\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + time.sleep(1) + received = True + except OSError as err: + print("OS error: {0}".format(err)) + + logger.info('Test failed...', + extra={'timegap': time_gap(TEST_START_TIME)}) + print() + + time.sleep(1) + + reset() + + received = False + + self.assertTrue(received) + + except TimeoutError as err: + print("TimeOut error: {0}".format(err)) + + logger.info('Connection is poor: Time out error...\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Test failed...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + print() + + time.sleep(1) + + reset() + + received = False + + self.assertTrue(received) + + finally: + logger.info('I2C Communication Ended...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + # Close Setup + + bus.close() + + time.sleep(1) + + logger.info('Connection Satisfied.', + extra={'timegap': time_gap(TEST_START_TIME)}) + received = True + + time.sleep(1) + + logger.info('I2C Slave Rx Test has ended.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() + + self.assertTrue(received) +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(I2CSlaveRxTest): + def setUp(self): + reset() + + logger.info('Setting up for nrf52840dk I2C Slave Rx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/i2c-slave-tx/Makefile b/examples/ci-tests/i2c-slave-tx/Makefile new file mode 100644 index 000000000..a38de484d --- /dev/null +++ b/examples/ci-tests/i2c-slave-tx/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/ci-tests/i2c-slave-tx/README.md b/examples/ci-tests/i2c-slave-tx/README.md new file mode 100644 index 000000000..c1b42c14e --- /dev/null +++ b/examples/ci-tests/i2c-slave-tx/README.md @@ -0,0 +1,22 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic test that sends message from slave to master, and master checks if message has been sent through a specific function. If function has been called with proper connection, then check if message sent is what's expected. If so, test passes. Otherwise, test will fail due to poor connection or exception thrown from function, or message sent was not the expected message. + +To run the test, +```bash +sudo python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test + +**NOTE** +This test requires the Raspberry Pi to be set as Master. Thus, you must use Broadcom Pins 2 & 3 (SDA & SCL resepectively) or GPIO pin 3 and 5. + +This test requires button activation on tested boards. Two jumpers are required for the activation of the reset button and user button on boards. Broadcom Pins on Raspberry PI used are pins 20 & 21 (GPIO Pins 38 & 40) which are used for user button and reset respectively. \ No newline at end of file diff --git a/examples/ci-tests/i2c-slave-tx/main.c b/examples/ci-tests/i2c-slave-tx/main.c new file mode 100644 index 000000000..c91062ef5 --- /dev/null +++ b/examples/ci-tests/i2c-slave-tx/main.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; +uint8_t slave_write_buf[BUF_SIZE]; +uint8_t slave_read_buf[BUF_SIZE]; +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_SLAVE_WRITE) { + printf("CB: Slave write\n"); + + printf("%s sending\n", is_leader ? "Leader" : "Follower"); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + printf("Message Sent...\n"); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Sending to master\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master/Slave Ping-Pong\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + strcpy((char*) slave_read_buf, "Hello friend.\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_write_buffer(slave_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_read_buffer(slave_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(FOLLOW_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} diff --git a/examples/ci-tests/i2c-slave-tx/test.py b/examples/ci-tests/i2c-slave-tx/test.py new file mode 100644 index 000000000..80d123cbf --- /dev/null +++ b/examples/ci-tests/i2c-slave-tx/test.py @@ -0,0 +1,178 @@ +# I2C Slave Tx Test +# This tester corresponds to libtock-c/examples/ci-tests/i2c-slave-tx test. + +from smbus import SMBus +import time +import os +import logging +import unittest +from gpiozero import InputDevice +from gpiozero import OutputDevice + +RESET = 21 # Broadcom pin 21 (P1 pin 40) +BUTTON_1 = 20 # Broadcom pin 20 (P1 pin 38) + +button = OutputDevice(BUTTON_1) +reset_button = OutputDevice(RESET) + +ADDRESS = 0x41 # bus address to the Slave Device +MASTER = 0x40 # Raspberry Pi Master Address +MESSAGE = "Hello friend." # Message sent from slave +BUF_SIZE = 16 +bus = SMBus(1) # indicates /dev/ic2-1 + +# cancelling slave configuration pins +pin_1 = InputDevice(10) +pin_2 = InputDevice(11) + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) + +def reset(): + global RESET + """Button is Reset""" + + reset_button.off() + time.sleep(1.1) + reset_button.on() + time.sleep(0.5) + +def press_button(): + global BUTTON_1 + """Button is one of User Buttons""" + + button.on() + time.sleep(1.1) + button.off() + time.sleep(0.5) + +def message_decoder(data): + string = '' + for item in data: + string += chr(item) + return string + +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('I2C Slave Tx Test') +logger.setLevel('INFO') + +logger.info('Initiating I2C Slave Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class I2CSlaveTxTest(unittest.TestCase): + def test_i2c_slave_tx_configuration(self): + + print() + logger.info('Communicating with I2C Device to Receive Message: ' + MESSAGE, + extra={'timegap': time_gap(TEST_START_TIME)}) + + received = False + + time.sleep(2) # Wait until app has fully reset + + press_button() # Used to press one of the user buttons on the board + + try: + dataM = bus.read_i2c_block_data(ADDRESS, MASTER, BUF_SIZE) + message = message_decoder(dataM) + message = str(message) + message_stripped = message.splitlines() + message_received = message_stripped[0] + + if(MESSAGE == message_received): + + logger.info('Message Received: ' + message_received, + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + logger.info('Message Sent Successfully from Slave\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Connection Satisfied.', + extra={'timegap': time_gap(TEST_START_TIME)}) + + time.sleep(1) + + received = True + + except OSError as err: + print("OS error: {0}".format(err)) + + logger.info('Test failed...', + extra={'timegap': time_gap(TEST_START_TIME)}) + print() + + time.sleep(1) + except TimeoutError: + logger.info('Connection is poor: Time out error...\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + + logger.info('Test failed...', + extra={'timegap': time_gap(TEST_START_TIME)}) + print() + + time.sleep(1) + finally: + logger.info('I2C Communication Ended...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + reset() # Reset application to stop sending messages + + bus.close() + + time.sleep(1) + + logger.info('I2C Slave Tx Test has ended.\n', + extra={'timegap': time_gap(TEST_START_TIME)}) + + self.assertTrue(received) +# END + +################################################################################ +# Test Case Setup +################################################################################ + +class Nrf52840Test(I2CSlaveTxTest): + def setUp(self): + reset() # Used to activate the reset button on board + + logger.info('Setting up for nrf52840dk I2C Slave Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/ci-tests/uartrt/Makefile b/examples/ci-tests/uartrt/Makefile new file mode 100644 index 000000000..a38de484d --- /dev/null +++ b/examples/ci-tests/uartrt/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/ci-tests/uartrt/README.md b/examples/ci-tests/uartrt/README.md new file mode 100644 index 000000000..0845dc970 --- /dev/null +++ b/examples/ci-tests/uartrt/README.md @@ -0,0 +1,17 @@ +# Hardware CI Tests + +## Run Python Test + +`test.py` provides basic test that send character over uart connection, and same expects character to be returned. Provides this test over a course of 45 seconds. If character returned was not the same in this buffer period, test will fail. Otherwise, correct character was returned and test will pass. + +To run the test, +```bash +sudo python3 test.py Nrf52840Test +``` + +Switch board name to the test you intend to run. Otherwise, python unittest +will attempt to run all tests. + +Board | Test Name +------|---------- +nrf52840dk | Nrf52840Test \ No newline at end of file diff --git a/examples/ci-tests/uartrt/main.c b/examples/ci-tests/uartrt/main.c new file mode 100644 index 000000000..341620648 --- /dev/null +++ b/examples/ci-tests/uartrt/main.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include + +char* echo_message = ""; +int main(void) { + + while (1) { + int c = getch(); + + char m = (char) c; + + char message[3]; + + message[0] = 'r'; + message[1] = m; + message[2] = '\0'; + + printf("\n"); + delay_ms(1000); + + putnstr(message, (strlen(message))); + + delay_ms(3000); + /*if(strcmp(echo_message, "") != 0){ + printf("\n Finishing loop"); + break; + }*/ + delay_ms(2000); + } + +} diff --git a/examples/ci-tests/uartrt/test.py b/examples/ci-tests/uartrt/test.py new file mode 100644 index 000000000..29e419370 --- /dev/null +++ b/examples/ci-tests/uartrt/test.py @@ -0,0 +1,131 @@ +import logging +import unittest +import serial +import time +import random +import struct + +TARGET_ACKNOWLEDGEMENT = "" +sp = None # Serial port for specific boards to read the echo of messages +ser = serial.Serial(port="/dev/ttyS0", baudrate=115200, bytesize=8, parity="N", stopbits=1) + + +print("Starting Uart Rx/TX Test...\n") + +################################################################################ +# Helper Functions +################################################################################ + +def time_gap(start_time): + """Return time gap between current time and start_time + Argument: + start_time - Start time + """ + return "{:.6f}".format(time.time() - start_time) +# END + +################################################################################ +# Start test and logger +################################################################################ + +# Test Start Time +TEST_START_TIME = time.time() + +# Logger set format +LOG_FORMAT = "%(timegap)s %(levelname)s -- %(message)s" +logging.basicConfig(format=LOG_FORMAT) + +# Logger add formatter +logger = logging.getLogger('UART Test') +logger.setLevel('INFO') + +logger.info('Initiating UART test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Test Case Module +################################################################################ + +class UartTest(unittest.TestCase): + def test_uart_rx_tx(self): + while(1): + logger.info('Waiting for message...', + extra={'timegap': time_gap(TEST_START_TIME)}) + + buffer_period = float(time_gap(TEST_START_TIME)) + if(buffer_period > 45.0): + sp.close() + ser.close() + self.assertTrue(False) + + c = random.randint(65, 90) + TARGET_ACKNOWLEDGEMENT = chr(c) + ser.write(struct.pack(' 0): + + logger.info('Message sent: ' + TARGET_ACKNOWLEDGEMENT, + extra={'timegap': time_gap(TEST_START_TIME)}) + sp.readline() + if(sp.in_waiting > 0): + + message_received = sp.readline() + message_received = message_received.decode("Ascii") + char_received = message_received[-1] + if(char_received == TARGET_ACKNOWLEDGEMENT): + + logger.info("Message Received (r[character]): " + message_received, + extra={'timegap': time_gap(TEST_START_TIME)}) + logger.info("Echoed: " + char_received, + extra={'timegap': time_gap(TEST_START_TIME)}) + print("Correct Serial Communication Message Received") + self.assertTrue(True) + sp.close() + ser.close() + break + +# END + +################################################################################ +# Test Case Setup +################################################################################ + + +class Nrf52840Test(UartTest): + def setUp(self): + global sp + + sp = serial.Serial(port="/dev/ttyACM0", baudrate=115200, bytesize=8, timeout=2) + logger.info('Setting up for nrf52840dk Uart Rx/Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +class HailTest(UartTest): + def setUp(self): + global sp + + sp = serial.Serial(port="/dev/ttyUSB0", baudrate=115200, bytesize=8, parity="N", stopbits=1) + logger.info('Setting up for hail Uart Rx/Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +class Nrf52Test(UartTest): + def setUp(self): + global sp + + sp = serial.Serial(port="/dev/ttyACM0", baudrate=115200, bytesize=8, timeout=2) + logger.info('Setting up for nrf52dk Uart Rx/Tx test...', + extra={'timegap': time_gap(TEST_START_TIME)}) + +# END + +################################################################################ +# Main +################################################################################ + +if __name__ == '__main__': + unittest.main() diff --git a/examples/sampletests/i2cexamples/Makefile b/examples/sampletests/i2cexamples/Makefile new file mode 100644 index 000000000..bc0885323 --- /dev/null +++ b/examples/sampletests/i2cexamples/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/main.c b/examples/sampletests/i2cexamples/main.c new file mode 100644 index 000000000..619d22d0e --- /dev/null +++ b/examples/sampletests/i2cexamples/main.c @@ -0,0 +1,66 @@ +#include +#include + +#include +#include +#include + +#define BUF_SIZE 6 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t slave_write_buf[BUF_SIZE]; +uint8_t slave_read_buf[BUF_SIZE]; + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + + printf("callback Num: %d\n", callback_type); + if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + printf("CB: Master write\n"); + } else if (callback_type == TOCK_I2C_CB_SLAVE_WRITE) { + printf("CB: Slave write\n"); + //printf("Read Buffer v2 is >%.*s<\n", BUF_SIZE, slave_read_buf); + //printf("Write Buffer v2 is >%.*s<\n", BUF_SIZE, slave_write_buf); + + delay_ms(2500); + + printf("%s sending\n", FOLLOW_ADDRESS); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(LEADER_ADDRESS, BUF_SIZE)); + printf("Write Buffer v2 is >%.*s<\n", BUF_SIZE, slave_write_buf); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +int main(void) { + printf("I2C Slave Read \n"); + + + strncpy(slave_write_buf, "012345", BUF_SIZE); + int buffer_to_write = {0,1,2,3,4,5}; + printf("Buffer is >%.*s<\n", BUF_SIZE, slave_write_buf); + // Prepare buffers + + // Set up I2C peripheral + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_write_buffer(slave_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_read_buffer(slave_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(FOLLOW_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write_sync(LEADER_ADDRESS, BUF_SIZE, buffer_to_write)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + + printf("Read Buffer is >%.*s<\n", BUF_SIZE, slave_read_buf); + printf("Write Buffer is >%.*s<\n", BUF_SIZE, slave_write_buf); +} diff --git a/examples/sampletests/i2cexamples/masterex/Makefile b/examples/sampletests/i2cexamples/masterex/Makefile new file mode 100644 index 000000000..c13e35823 --- /dev/null +++ b/examples/sampletests/i2cexamples/masterex/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/masterex/main.c b/examples/sampletests/i2cexamples/masterex/main.c new file mode 100644 index 000000000..495228598 --- /dev/null +++ b/examples/sampletests/i2cexamples/masterex/main.c @@ -0,0 +1,100 @@ +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + printf("CB: Master write\n"); + + delay_ms(2500); + + printf("Sending: Hello friend.\n"); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Switching to master\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + printf("Switched\n"); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master Write App!\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(LEADER_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/masterread/Makefile b/examples/sampletests/i2cexamples/masterread/Makefile new file mode 100644 index 000000000..c13e35823 --- /dev/null +++ b/examples/sampletests/i2cexamples/masterread/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/masterread/main.c b/examples/sampletests/i2cexamples/masterread/main.c new file mode 100644 index 000000000..db604ed96 --- /dev/null +++ b/examples/sampletests/i2cexamples/masterread/main.c @@ -0,0 +1,110 @@ +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; + +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_READ) { + printf("Master Reader\n"); + delay_ms(2500); + + printf("Read Buffer After: >%.*s<\n", BUF_SIZE, master_read_buf); + memcpy((char*) master_write_buf, (char*) master_read_buf, BUF_SIZE); + //master_write_buf[BUF_SIZE - 1] = '\0'; + printf("Write Buf Now: >%.*s<\n", BUF_SIZE, master_write_buf); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + delay_ms(1500); + + printf("Sending; >%.*s<\n", BUF_SIZE, master_write_buf); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Getting Message\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_read(FOLLOW_ADDRESS, BUF_SIZE)); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master Read Test\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + strcpy((char*) master_read_buf, "Hello I Master\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(LEADER_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + printf("Read Buffer Before: >%.*s<\n", BUF_SIZE, master_read_buf); + + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/slavewrite/Makefile b/examples/sampletests/i2cexamples/slavewrite/Makefile new file mode 100644 index 000000000..c13e35823 --- /dev/null +++ b/examples/sampletests/i2cexamples/slavewrite/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/slavewrite/main.c b/examples/sampletests/i2cexamples/slavewrite/main.c new file mode 100644 index 000000000..f839cb69c --- /dev/null +++ b/examples/sampletests/i2cexamples/slavewrite/main.c @@ -0,0 +1,139 @@ +#include +#include + +#include +#include +#include + +#define BUF_SIZE 6 +#define FOLLOW_ADDRESS 0x41 +#define LEADER_ADDRESS 0x40 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; +uint8_t slave_write_buf[BUF_SIZE]; +uint8_t slave_read_buf[BUF_SIZE]; +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + printf("CB: Slave Read\n"); + + delay_ms(1000); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(LEADER_ADDRESS, BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write_read(FOLLOW_ADDRESS, BUF_SIZE, BUF_SIZE)); + } else if (callback_type == TOCK_I2C_CB_SLAVE_WRITE) { + printf("CB: Slave write\n"); + //delay_ms(2500); + + printf("Write Buffer v2 is >%.*s<\n", BUF_SIZE, slave_write_buf); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write_read(FOLLOW_ADDRESS, BUF_SIZE, BUF_SIZE)); + } else if (callback_type == TOCK_I2C_CB_SLAVE_READ_REQUEST) { + printf("CB: Slave write\n"); + delay_ms(2500); + + printf("%s sending\n", is_leader ? "Leader" : "Follower"); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(LEADER_ADDRESS, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else if (callback_type == 7) { + printf("CB: Slave write 7\n"); + //delay_ms(2500); + + printf("Write Buffer v2 is >%.*s<\n", BUF_SIZE, slave_write_buf); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Sending to master\n"); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(LEADER_ADDRESS, BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write_read(FOLLOW_ADDRESS, BUF_SIZE, BUF_SIZE)); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Slave Sender"); + + // Prepare buffers + // BUF_SIZE = 16 + //strcpy((char*) master_write_buf, "Hello friend.\n"); + //strcpy((char*) slave_write_buf, "Hello friend.\n"); + + // BUF_SIZE = 6 BELOW + strcpy((char*) master_write_buf, "Hello"); + strcpy((char*) slave_write_buf, "Hello"); + strcpy((char*) slave_read_buf, "Hello"); + + printf("Buffer is >%.*s<\n", BUF_SIZE, slave_write_buf); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_write_buffer(slave_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_read_buffer(slave_read_buf, BUF_SIZE)); + + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(FOLLOW_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/slavewritev2/Makefile b/examples/sampletests/i2cexamples/slavewritev2/Makefile new file mode 100644 index 000000000..c13e35823 --- /dev/null +++ b/examples/sampletests/i2cexamples/slavewritev2/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/sampletests/i2cexamples/slavewritev2/main.c b/examples/sampletests/i2cexamples/slavewritev2/main.c new file mode 100644 index 000000000..41d5bd443 --- /dev/null +++ b/examples/sampletests/i2cexamples/slavewritev2/main.c @@ -0,0 +1,109 @@ +#include +#include + +#include +#include +#include + +#define BUF_SIZE 16 +#define LEADER_ADDRESS 0x40 +#define FOLLOW_ADDRESS 0x41 + +uint8_t master_write_buf[BUF_SIZE]; +uint8_t master_read_buf[BUF_SIZE]; +uint8_t slave_write_buf[BUF_SIZE]; +uint8_t slave_read_buf[BUF_SIZE]; +bool is_leader = false; + + +// In response to a +static void i2c_callback(int callback_type, + __attribute__ ((unused)) int length, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Watching for GPIO interrupts holds us in a higher power state, so stop + // doing that once we don't care about button presses any more (the first + // time having sent or received a message) + static bool any_message = false; + if (!any_message) { + int nbuttons; + button_count(&nbuttons); + int j; + for (j = 0; j < nbuttons; j++) { + button_disable_interrupt(j); + } + } + + if (callback_type == TOCK_I2C_CB_MASTER_WRITE) { + printf("CB: Master write\n"); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + } else if (callback_type == TOCK_I2C_CB_SLAVE_WRITE) { + printf("CB: Slave write\n"); + //delay_ms(300); + + printf("%s sending\n", is_leader ? "Leader" : "Follower"); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(is_leader ? FOLLOW_ADDRESS : LEADER_ADDRESS, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + } else { + printf("ERROR: Unexepected callback: type %d\n", callback_type); + } +} + +// This is the callback for the button press. +// A button press indicates that this device should start the ping-pong +// exchange. First, change the address to the BUTTON_ADDRESS to avoid +// conflict with the other node, then send a message. +static void button_cb(__attribute__((unused)) int btn_num, + __attribute__ ((unused)) int arg1, + __attribute__ ((unused)) int arg2, + __attribute__ ((unused)) void* userdata) { + // Only the first press is meaningfull + static bool pressed = false; + + if (!pressed) { + pressed = true; + is_leader = true; + + printf("Sending to master\n"); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_enable_slave_read(BUF_SIZE)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(LEADER_ADDRESS)); + //TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_write(FOLLOW_ADDRESS, BUF_SIZE)); + } +} + +// This function sets up the I2C peripheral with needed buffers and prepares +// callbacks for I2C and button presses. Normal operation of this test takes +// place in the subsequent callbacks. +int main(void) { + printf("I2C Master/Slave Ping-Pong\n"); + + // Prepare buffers + strcpy((char*) master_write_buf, "Hello friend.\n"); + strcpy((char*) slave_read_buf, "Hello friend.\n"); + + // Set up I2C peripheral + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_callback(i2c_callback, NULL)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_write_buffer(master_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_master_read_buffer(master_read_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_write_buffer(slave_write_buf, BUF_SIZE)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_read_buffer(slave_read_buf, BUF_SIZE)); + + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_set_slave_address(FOLLOW_ADDRESS)); + TOCK_EXPECT(RETURNCODE_SUCCESS, i2c_master_slave_listen()); + + // Set up button peripheral to grab any button press + TOCK_EXPECT(RETURNCODE_SUCCESS, button_subscribe(button_cb, NULL)); + + int nbuttons; + button_count(&nbuttons); + if (nbuttons < 1) { + printf("ERROR: This app requires that a board have at least one button.\n"); + exit(-1); + } + + int j; + for (j = 0; j < nbuttons; j++) { + TOCK_EXPECT(RETURNCODE_SUCCESS, button_enable_interrupt(j)); + } +} \ No newline at end of file diff --git a/simple-ble/build/cortex-m0/simple-ble.a b/simple-ble/build/cortex-m0/simple-ble.a index 3c89a21d5..31ee42c5b 100644 Binary files a/simple-ble/build/cortex-m0/simple-ble.a and b/simple-ble/build/cortex-m0/simple-ble.a differ diff --git a/simple-ble/build/cortex-m3/simple-ble.a b/simple-ble/build/cortex-m3/simple-ble.a index 35c44a335..2d52a6b6d 100644 Binary files a/simple-ble/build/cortex-m3/simple-ble.a and b/simple-ble/build/cortex-m3/simple-ble.a differ diff --git a/simple-ble/build/cortex-m4/simple-ble.a b/simple-ble/build/cortex-m4/simple-ble.a index 8b811e217..6b3174b1c 100644 Binary files a/simple-ble/build/cortex-m4/simple-ble.a and b/simple-ble/build/cortex-m4/simple-ble.a differ diff --git a/simple-ble/build/cortex-m7/simple-ble.a b/simple-ble/build/cortex-m7/simple-ble.a index d1be03fe0..cc64e12f8 100644 Binary files a/simple-ble/build/cortex-m7/simple-ble.a and b/simple-ble/build/cortex-m7/simple-ble.a differ