Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
test-and-measurement:analog-discovery-pro-3x50:smart-lamp [2021/10/18 07:22] – [Building a Battery Powered Smart Lamp with the Analog Discovery Pro (ADP3450/ADP3250)] Álmos Veres-Vitályostest-and-measurement:analog-discovery-pro-3x50:smart-lamp [2022/09/12 18:30] (current) – changed forum.digilentinc.com to forum.digilent.com Jeffrey
Line 10: Line 10:
 ===== Planning ===== ===== Planning =====
 <WRAP group> <WRAP group>
-Before starting designing the lamp, the goals of the project must be established. The central component of the lamp is an RGB LED which should be switched on/off via an application running on a phone. Communication with the phone can be resolved using Bluetooth or Bluetooth Low Energy, but the Low Energy variant (BLE) can be easier to use in some cases, due to the custom service characteristics (more on this later). The application should also display the ambient light conditions to notify the user to turn off the lamp if it isn't needed (automatic switching may be implemented if needed). To power the lamp, a battery can be used which can be charged from the Analog Discovery Pro (the lamp can't be directly powered from the ADP, as it doesn't provide enough current).+Before starting designing the lamp, the goals of the project must be established. The central component of the lamp is an RGB LED which should be switched on/off via an application running on a phone. Communication with the phone can be resolved using Bluetooth or Bluetooth Low Energy, but the Low Energy variant (BLE) can be easier to use in some cases, due to the custom service characteristics (more on this later). To power the lamp, a battery can be used which can be charged from the Analog Discovery Pro (the lamp can't be directly powered from the ADP, as it doesn't provide enough current).
  
-In this prototype, a 5 mm RGB LED will be used as the lamp, which is powered by an old phone battery, but the circuit can be scaled for higher power lamps if needed. Communication with the phone is resolved by the [[pmod:pmodble:start|]] (Bluetooth Low Energy) module and ambient light measurements are performed by the [[pmod:pmodals:start|]] (Ambient Light Sensor). The [[pmod:pmodda1:start|]] (Digital to Analog Converter) and the [[pmod:pmodod1:start|]] (Open Drain MOSFETs) will be used in the control circuit. A full inventory of the components needed is listed below.+In this prototype, a 5 mm RGB LED will be used as the lamp, which is powered by an old phone battery, but the circuit can be scaled for higher power lamps if needed. Communication with the phone is resolved by the [[pmod:pmodble:start|]] (Bluetooth Low Energy). The [[pmod:pmodals:start|]] is used to provide feedback from the lamp and to resolve automatic switching. A full inventory of the components needed is listed below.
 </WRAP> </WRAP>
 ---- ----
Line 23: Line 23:
   * [[pmod:pmodble:start|]]   * [[pmod:pmodble:start|]]
   * [[pmod:pmodals:start|]]   * [[pmod:pmodals:start|]]
-  * [[pmod:pmodda1:start|]] 
-  * [[pmod:pmodod1:start|]] 
   * Lithium-Polymer (LiPo) battery cell   * Lithium-Polymer (LiPo) battery cell
   * RGB LED   * RGB LED
-  * USB-A to Micro-USB cable+  * battery charger circuit
   * [[https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/EVAL-ADALP2000.html#eb-overview|ADALP2000 Analog Parts Kit]]   * [[https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/EVAL-ADALP2000.html#eb-overview|ADALP2000 Analog Parts Kit]]
-    * USB connector 
-    * LT3092 programmable current source 
     * OP484 operational amplifier     * OP484 operational amplifier
-    * 5mm LED +    * 3x 1KΩ resistor 
-    * 470Ω resistor +    * 3x 180Ω resistor 
-    * 3x 100Ω resistor +    * 3x 47μF capacitor 
-    * 47Ω resistor +    * 3x 2N3904 NPN transistor
-    * 10Ω resistor+
 </WRAP><WRAP half column> </WRAP><WRAP half column>
 == Software == == Software ==
Line 42: Line 37:
   * [[https://code.visualstudio.com/|Visual Studio Code]], or any other editor of your choice   * [[https://code.visualstudio.com/|Visual Studio Code]], or any other editor of your choice
   * a web browser   * a web browser
 +  * a terminal emulator
  
 **Note:** //WaveForms can be installed by following the [[software:waveforms:waveforms-3:getting-started-guide]].// **Note:** //WaveForms can be installed by following the [[software:waveforms:waveforms-3:getting-started-guide]].//
Line 49: Line 45:
 ===== Creating a Hardware Abstraction Layer (HAL) for the Analog Discovery Pro ===== ===== Creating a Hardware Abstraction Layer (HAL) for the Analog Discovery Pro =====
 <WRAP group> <WRAP group>
-A Hardware Abstraction Layer (HAL) is a layer in programming that takes the functions controlling the hardware and makes them more abstract, by generalizing them. In this case, the hardware, the Analog Discovery Pro, is controlled using WaveForms SDK, from a Python script. WaveForms SDK requires a fair amount of stuff to happen to start using an instrument. The SDK uses the ctypes module and most instruments need several initialization functions (like dummy read/write functions in the case of some digital communication protocols) before using them. As the current project uses several instruments, some sort of module is needed to make the interaction with the hardware more straightforward. In the following, a Python module will be created, which is based on WaveForms SDK and works like an instrument driver for the Analog Discovery Pro. If you are not interested in the details of this module, skip to the next section. The library containing the modules can be downloaded here: {{ :test-and-measurement:analog-discovery-pro-3x50:waveforms_hal.zip |waveforms_hal.zip}}.+A Hardware Abstraction Layer (HAL) is a layer in programming that takes the functions controlling the hardware and makes them more abstract, by generalizing them. In this case, the hardware, the Analog Discovery Pro, is controlled using WaveForms SDK, from a Python script. WaveForms SDK requires a fair amount of stuff to happen to start using an instrument. The SDK uses the ctypes module and most instruments need several initialization functions (like dummy read/write functions in the case of some digital communication protocols) before using them. As the current project uses several instruments, some sort of module is needed to make the interaction with the hardware more straightforward. 
 + 
 +You can use the module presented in the [[test-and-measurement:guides:waveforms-sdk-getting-started|]] guide, or you can follow the steps presented there to write your own instrument driver module.
 </WRAP> </WRAP>
  
-<WRAP center round important 60%>+/*<WRAP center round important 60%>
 In the attached package, not all the functions were tested, so errors might appear in some cases. Use the package responsibly and feel free to modify it. In the attached package, not all the functions were tested, so errors might appear in some cases. Use the package responsibly and feel free to modify it.
 </WRAP> </WRAP>
Line 1422: Line 1420:
 <-- <--
  
-</WRAP>+</WRAP>*/
  
 ---- ----
  
-===== Creating a Library for the Pmods =====+===== Creating a Module for the Pmods =====
 <WRAP group> <WRAP group>
-Now a library should be created to control the Pmods easily. These Pmod drivers will be created in a similar way to the HAL, but will use it to control the hardware. If you are not interested in the details of this moduleskip to the next section. The library containing the modules can be downloaded {{ :test-and-measurement:analog-discovery-pro-3x50:pmod.zip |here}}.+The Pmod drivers will be created in a similar way to the HAL, but will use it to control the hardware. Download the respective filesthen continue at the next section
 + 
 +As the Pmod BLE communicates via UART, the read and write functions will be implemented in two ways: one method uses the UART instrument to send and receive data, the other uses the logic analyzer and the pattern generator instruments to implement UART communication. This way the device will be able to use the "less smart" method not to interrupt PWM generation for the LEDs. The module can be downloaded {{ :test-and-measurement:analog-discovery-pro-3x50:pmod_ble.zip |here}}. 
 + 
 +The Pmod ALS communicates via SPI, but along the read and write functions which use the protocol instrument, alternate functions using the static I/O instrument must be implemented for the same reasons. The module can be downloaded {{ :test-and-measurement:analog-discovery-pro-3x50:pmod_als.zip |here}}.
 </WRAP> </WRAP>
  
 <WRAP center round important 60%> <WRAP center round important 60%>
-In the attached package, not all the functions were tested, so errors might appear in some cases. Use the package on your own responsibility and feel free to modify it. +In the attached packages, not all the functions were tested, so errors might appear in some cases. Use the package on your own responsibility and feel free to modify it.
-</WRAP> +
- +
- +
-<WRAP group> +
-Create a new folder and inside create an initializer file and separate files for every Pmod you want to use. +
- +
---> Wrapper: __init__.py # +
-<WRAP group><WRAP half column> +
-This module imports all submodules and names them +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with multiple PMODs """ +
- +
-""" WRAPPER """ +
- +
-from PMOD import PMOD_ALS as ALS +
-from PMOD import PMOD_BLE as BLE +
-from PMOD import PMOD_DA1 as DA1 +
-from PMOD import PMOD_KYPD as KYPD +
-from PMOD import PMOD_OD1 as OD1 +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
- +
---> Pmod ALS: PMOD_ALS.py # +
-<WRAP group><WRAP half column> +
-The ambient light sensor uses the SPI interface for communication, with three lines (chip select, serial data out and serial clock) and a clock frequency between 1MHz and 4MHz. The on-board analog to digital converter returns 2 bytes of data, most significant bit first, which contain 3 leading and 4 trailing zeros. The sensor saturates at the output value 127. The module also converts the raw data into percentage. +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with the Pmod ALS """ +
- +
-"""-------------------------------------------------------------------""" +
-""" VARIABLES """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-class pins: +
-    cs = 0  # CS pin of the Pmod +
-    sdo = 1  # SDO pin of the Pmod +
-    sck = 2  # SCK pin of the Pmod +
- +
- +
-# WaveForms SDK HAL object +
-WF = None +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def initialize(WaveForms_HAL): +
-    global WF +
-    WF = WaveForms_HAL +
- +
-    # start the power supply +
-    WF.supply.switch(True) +
- +
-    # initialize the SPI interface +
-    WF.spi.initialize(cs_pin=pins.cs, sck_pin=pins.sck, +
-                      miso_pin=pins.sdo, frequency=1e06) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" CLEANUP """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def close(): +
-    pass +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" RECEIVE DATA """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def receive_data(): +
-    data = WF.spi.receive(2)   # read 2 bytes +
-    msb = ord(data[0]) & 0xFF +
-    lsb = ord(data[1]) & 0xFF +
-    # concatenate bytes without trailing and leading zeros +
-    result = ((msb << 3) | (lsb >> 4)) & 0xFF +
-    return result +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" RECEIVE PERCENTAGE """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def receive_percent(): +
-    # receive and convert raw data +
-    data = receive_data() * 100 / 127 +
-    return round(data, 2) +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
- +
---> Pmod BLE: PMOD_BLE.py # +
-<WRAP group><WRAP half column> +
-The Pmod BLE is a Bluetooth Low Energy module, which communicates on UART interface, using a 115200bps baud rate. The controlling module contains functions to factory reset the device by putting it into command mode and sending the necessary commands. Besides that, it also can send and receive data, decoding the received bytes and separating the buffer into a list which contains only data and one which contains only system messages (starting and ending with "%"). +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with the Pmod BLE """ +
- +
-import time +
- +
-"""-------------------------------------------------------------------""" +
-""" VARIABLES """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-class pins: +
-    rx = 0  # TX pin of the Pmod +
-    tx = 1  # RX pin of the Pmod +
-    rst = 2  # RESET pin of the Pmod +
- +
- +
-class commands: +
-    command_mode = "$$$"    # enter command mode +
-    data_mode = "---\r"  # enter data mode +
-    rename = "S-,PmodBLE\r"  # set name to PmodBLE_XXXX +
-    factory_reset = "SF,1\r"    # factory reset +
-    high_power = "SGA,0\r"  # set high power output +
-    code = "SP,123456\r"    # set pin code to "123456" +
-    mode = "SS,C0\r"    # support device info + UART Transparent +
-    reboot = "R,1\r"    # reboots the device +
- +
- +
-# WaveForms SDK HAL object +
-WF = None +
- +
-# variables for detecting system messages +
-currently_sys = False +
-previous_msg = "" +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def initialize(WaveForms_HAL, first_run=False): +
-    global WF +
-    WF = WaveForms_HAL +
- +
-    # start the power supply +
-    WF.supply.switch(True) +
- +
-    # initialize the UART interface +
-    WF.uart.initialize(rx_pin=pins.tx, tx_pin=pins.rx, baud_rate=115200) +
- +
-    # factory reset the Pmod +
-    if first_run: +
-        reset() +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" REBOOT """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def reboot(): +
-    # hard reset the device +
-    # pull down the reset line +
-    WF.digital.write(pins.rst, False) +
-    # wait +
-    time.sleep(1) +
-    # pull up the reset line +
-    WF.digital.write(pins.rst, True) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" RESET """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def reset(): +
-    # enter command mode +
-    send_command(commands.command_mode) +
-    time.sleep(3) +
- +
-    # factory reset the Pmod +
-    success = send_command(commands.factory_reset) +
-    while success != True: +
-        success = send_command(commands.factory_reset) +
-        time.sleep(1) +
- +
-    # enter command mode +
-    send_command(commands.command_mode) +
-    time.sleep(3) +
- +
-    # rename device +
-    send_command(commands.rename) +
-    time.sleep(1) +
- +
-    # set high power mode +
-    send_command(commands.high_power) +
-    time.sleep(1) +
- +
-    # set communication mode +
-    send_command(commands.mode) +
-    time.sleep(1) +
- +
-    # exit command mode +
-    send_command(commands.data_mode) +
-    time.sleep(3) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" CLEANUP """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def close(): +
-    # restart the module +
-    reboot() +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SEND COMMAND """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def send_command(command): +
-    # send the command +
-    send_data(command) +
-    # record response +
-    response, _, error = receive_data +
-    # analyze response +
-    response = response[0:3] +
-    if response == "ERR" or response == "Err" or error != "no error": +
-        return False +
-    return True +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SEND DATA """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def send_data(data): +
-    # send data +
-    WF.uart.send(data) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" RECEIVE DATA """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def receive_data(): +
-    global previous_msg, currently_sys +
- +
-    # record incoming message +
-    data, error = WF.uart.receive() +
-    sys_msg = "" +
- +
-    # check for system messages +
-    special = data.count("%"+
- +
-    """---------------------------------""" +
- +
-    if special == 0: +
-        if currently_sys: +
-            # the middle of a system message +
-            previous_msg = previous_msg + data +
-            data = "" +
-        else: +
-            # not a system message +
-            pass +
- +
-        """---------------------------------""" +
- +
-    elif special == 2: +
-        # clear system message +
-        sys_msg = data +
-        data = "" +
-        currently_sys = False +
-        previous_msg = "" +
- +
-        """---------------------------------""" +
- +
-    else: +
-        # fragmented system message +
-        data_list = data.split("%"+
- +
-        if currently_sys: +
-            # the end of the message +
-            sys_msg = previous_msg + data_list[0] + "%" +
-            currently_sys = False +
-            previous_msg = "" +
-            data = data_list[1] +
- +
-        else: +
-            # the start of the message +
-            currently_sys = True +
-            previous_msg = "%" + data_list[1] +
-            data = data_list[0] +
- +
-    return data, sys_msg, error +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
- +
---> Pmod DA1: PMOD_DA1.py # +
-<WRAP group><WRAP half column> +
-The digital to analog converter also communicates via the SPI interface. The Pmod DA1 includes two DAC chips that have common chip select and serial clock pins. Both chips contain two 8-bit DAC channels, so in total, the Pmod features four different conversion channels. The program computes the 8-bit data word from the voltage, then appends it to the command word specific to the DAC channel. To use chip 1, or chip 2, the command and data words must be sent on the data 0, or data 1 lines. +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with the Pmod DA1 """ +
- +
-"""-------------------------------------------------------------------""" +
-""" VARIABLES """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-class pins: +
-    sync = 0  # CS pin of the Pmod +
-    d0 = 1  # data 0 pin of the Pmod +
-    d1 = 2  # data 0 pin of the Pmod +
-    sck = 3  # SCK pin of the Pmod +
- +
- +
-class output_channel: +
-    # channels of the digital to analog converter +
-    A1 = 0 +
-    B1 = 1 +
-    A2 = 2 +
-    B2 = 3 +
- +
- +
-# voltage limits in Volts +
-max_voltage = 3.3 +
-min_voltage = 0 +
- +
-# WaveForms SDK HAL object +
-WF = None +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def initialize(WaveForms_HAL): +
-    global WF +
-    WF = WaveForms_HAL +
- +
-    # start the power supply +
-    WF.supply.switch(True) +
- +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" CLEANUP """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def close(): +
-    pass +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SEND VOLTAGE """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def output_voltage(channel, voltage): +
-    # select DAC chip +
-    mosi = pins.d0 +
-    if channel == output_channel.A2 or channel == output_channel.B2: +
-        mosi = pins.d1 +
- +
-    # initialize the SPI interface +
-    WF.spi.initialize(cs_pin=pins.sync, sck_pin=pins.sck, +
-                      mosi_pin=mosi, frequency=1e06) +
- +
-    # limit and encode voltage +
-    voltage = max(min(voltage, max_voltage), min_voltage) +
-    data = round(voltage * 255 / 3.3) & 0xFF +
- +
-    # select channel +
-    command = 3 +
-    if channel == output_channel.B1 or channel == output_channel.B2: +
-        command = 7 +
- +
-    # send both words +
-    WF.spi.send([command, data]) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
- +
---> Pmod KYPD: PMOD_KYPD.py # +
-<WRAP group><WRAP half column> +
-This module uses the static I/O instrument to read the current state of the keypad and returns the pressed keys. +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with the Pmod KYPD """ +
- +
-"""-------------------------------------------------------------------""" +
-""" VARIABLES """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-class pins: +
-    rows = [7, 6, 5, 4]    # pins connected to keypad rows +
-    columns = [3, 2, 1, 0]  # pins connected to keypad columns +
- +
- +
-# the layout of the keypad +
-keymap = [["1", "2", "3", "A"], +
-          ["4", "5", "6", "B"], +
-          ["7", "8", "9", "C"], +
-          ["0", "F", "E", "D"]] +
- +
- +
-# WaveForms SDK HAL object +
-WF = None +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def initialize(WaveForms_HAL): +
-    global WF +
-    WF = WaveForms_HAL +
- +
-    # enable the power supply +
-    WF.supply.switch(True) +
- +
-    # for every pin connected to a row +
-    for pin in pins.rows: +
-        WF.digital.set_output(pin)  # set the pin as output +
-        WF.digital.write(pin, False)    # set a starting state of LOW +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" CLEANUP """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def close(): +
-    pass +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" GETTING THE KEYPAD STATE """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def receive_data(): +
-    # get the current state of the Pmod KYPD +
-    result = []  # buffer to store the results +
- +
-    # go through all the rows +
-    for index_1 in range(len(pins.rows)): +
-        # set the state of every row +
-        for index_2 in range(len(pins.rows)): +
-            # only one row is HIGH at every moment +
-            if index_1 == index_2: +
-                # set the current row HIGH +
-                WF.digital.write(pins.rows[index_2], True) +
-            else: +
-                # set the current row LOW +
-                WF.digital.write(pins.rows[index_2], False) +
- +
-        # check every column +
-        for index_2 in range(len(pins.columns)): +
-            # if the current column is HIGH +
-            if WF.digital.read(pins.columns[index_2]): +
-                # append the respective key to the results +
-                result.append(keymap[index_1][index_2]) +
- +
-    # return the results +
-    return result +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
- +
---> Pmod OD1: PMOD_OD1.py # +
-<WRAP group><WRAP half column> +
-The Pmod OD1 contains four open drain MOSFETs, which act like switches: if a gate pin is set HIGH (True), the respective MOSFET grounds the connected line. If the gate pin is set to LOW (False), the MOSFET input (the drain) has high impedance. The module realizes functions for turning the switches on/off, toggling and driving them with a pulse width modulated (PWM) signal. When driving the MOSFETs with the PWM signal, the type of signal generation (static, or pattern generator) can be defined as a parameter. +
-</WRAP><WRAP half column> +
-<code python> +
-""" This module realizes communication with the Pmod ALS """ +
- +
-"""-------------------------------------------------------------------""" +
-""" VARIABLES """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-class pins: +
-    g1 = 0  # G1 pin of the Pmod +
-    g2 = 1  # G2 pin of the Pmod +
-    g3 = 2  # G3 pin of the Pmod +
-    g4 = 3  # G4 pin of the Pmod +
- +
- +
-# WaveForms SDK HAL object +
-WF = None +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" INITIALIZATION """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def initialize(WaveForms_HAL): +
-    global WF +
-    WF = WaveForms_HAL +
- +
-    # start the power supply +
-    WF.supply.switch(True) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" CLEANUP """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def close(): +
-    WF.static_pattern.reset() +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SWITCH ON """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def on(channel): +
-    pin = None +
-    if channel == 1: +
-        pin = pins.g1 +
-    elif channel == 2: +
-        pin = pins.g2 +
-    elif channel == 3: +
-        pin = pins.g3 +
-    elif channel == 4: +
-        pin = pins.g4 +
- +
-    if pin != None: +
-        # initialize the pins +
-        WF.static_pattern.stop(pin) +
-        WF.digital.set_output(pin) +
-        # switch on MOSFET +
-        WF.digital.write(pin, True) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SWITCH OFF """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def off(channel): +
-    pin = None +
-    if channel == 1: +
-        pin = pins.g1 +
-    elif channel == 2: +
-        pin = pins.g2 +
-    elif channel == 3: +
-        pin = pins.g3 +
-    elif channel == 4: +
-        pin = pins.g4 +
- +
-    if pin != None: +
-        # initialize the pins +
-        WF.static_pattern.stop(pin) +
-        WF.digital.set_output(pin) +
-        # switch off MOSFET 4 +
-        WF.digital.write(pin, False) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" SWITCH """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def switch(channel): +
-    pin = None +
-    if channel == 1: +
-        pin = pins.g1 +
-    elif channel == 2: +
-        pin = pins.g2 +
-    elif channel == 3: +
-        pin = pins.g3 +
-    elif channel == 4: +
-        pin = pins.g4 +
- +
-    if pin != None: +
-        # initialize the pins +
-        WF.static_pattern.stop(pin) +
-        WF.digital.set_output(pin) +
-        # switch off MOSFET 4 +
-        state = WF.digital.read(pin) +
-        WF.digital.write(pin, not state) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
-""" DRIVE MOSFET WITH PWM SIGNAL """ +
-"""-------------------------------------------------------------------""" +
- +
- +
-def drive(channel, frequency, duty_cycle, static=False): +
-    # enable/disable running in the background +
-    instrument = WF.pattern +
-    if static: +
-        instrument = WF.static_pattern +
- +
-    if channel == 1: +
-        # drive MOSFET 1 +
-        instrument.generate(pins.g1, frequency, +
-                            instrument.type.pulse, duty_cycle=duty_cycle) +
-    elif channel == 2: +
-        # drive MOSFET 2 +
-        instrument.generate(pins.g2, frequency, +
-                            instrument.type.pulse, duty_cycle=duty_cycle) +
-    elif channel == 3: +
-        # drive MOSFET 3 +
-        instrument.generate(pins.g3, frequency, +
-                            instrument.type.pulse, duty_cycle=duty_cycle) +
-    elif channel == 4: +
-        # drive MOSFET 4 +
-        instrument.generate(pins.g4, frequency, +
-                            instrument.type.pulse, duty_cycle=duty_cycle) +
-    return +
- +
- +
-"""-------------------------------------------------------------------""" +
- +
-</code> +
-</WRAP></WRAP> +
-<-- +
 </WRAP> </WRAP>
 ---- ----
  
-===== Testing the Modules =====+==== Testing the Pmods ====
 <WRAP group> <WRAP group>
-To make sure that the previously created Python modules work (and to provide examples for later usage), they should be tested. If you are not interested in the details of this testing, skip to the next section. The test scripts can be downloaded {{ :test-and-measurement:analog-discovery-pro-3x50:tests.zip |here}}.+To make sure that the previously created Python modules works (and to provide examples for later usage), they should be tested.
 </WRAP> </WRAP>
  
 <WRAP group> <WRAP group>
-Create test files for all Pmods and the Analog and Digital input-output instruments in the same folder, where the //WaveForms_HAL// and //PMOD// folders are. +--> Testing the Pmod BLE ^#
- +
---> Testing the Oscilloscope and the Waveform Generator: Test_HAL_Analog.py #+
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-To test the functionality of the analog input-output modules, first connect an oscilloscope channel to one of the waveform generator's channels, then output a DC voltage on the waveform generator and measure it with the scope. An AC signal should also be outputted and read back with the scope, then plotted using the matplotlib module. 
- 
-{{ :test-and-measurement:analog-discovery-pro-3x50:analog_test.png?400 |}} 
-</WRAP><WRAP half column> 
-<code python> 
-# import modules 
-import WaveForms_HAL as WF 
-import matplotlib.pyplot as plt 
- 
-# define used channels 
-scope_ch = 1 
-wavegen_ch = 1 
- 
-# define signal parameters 
-dc_voltage = 2.25 
-sin_amplitude = 2.0 
-sin_frequency = 50e03 
- 
-try: 
-    # initialize the interface 
-    WF.initialize() 
- 
-    # initialize the scope with default settings 
-    WF.scope.initialize() 
- 
-    # output a DC voltage 
-    WF.wavegen.generate(wavegen_ch, WF.wavegen.type.dc, dc_voltage) 
-    # display measured voltage level 
-    print(WF.scope.measure(scope_ch)) 
- 
-    # output a sine signal 
-    WF.wavegen.generate(wavegen_ch, WF.wavegen.type.sine, 
-                        amplitude=sin_amplitude, frequency=sin_frequency) 
- 
-    while True: 
-        plt.plot(WF.scope.receive(scope_ch))    # plot recorded signal 
-        plt.show() 
- 
-except KeyboardInterrupt: 
-    pass 
-finally: 
-    # close device 
-    WF.close() 
- 
-</code> 
-</WRAP></WRAP> 
-<-- 
- 
---> Testing the Logic Analyzer and the Pattern Generator: Test_HAL_Digital.py # 
-<WRAP group><WRAP half column> 
-To test the functionality of the digital input-output modules, a PWM signal is generated on one of the digital input-output channels. The signal is read back on the same pin, then plotted using the matplotlib module. 
- 
-{{ :test-and-measurement:analog-discovery-pro-3x50:digital_test.png?400 |}} 
-</WRAP><WRAP half column> 
-<code python> 
-# import modules 
-import WaveForms_HAL as WF 
-import matplotlib.pyplot as plt 
- 
-# define used digital pin 
-pin = 1 
- 
-# define signal parameters 
-frequency = 50e03 
-duty_cycle = 30 
- 
-try: 
-    # initialize the interface 
-    WF.initialize() 
- 
-    # initialize the logic analyzer with default settings 
-    WF.logic.initialize() 
- 
-    # generate a signal 
-    WF.pattern.generate( 
-        pin, frequency, WF.pattern.type.pulse, duty_cycle=duty_cycle) 
- 
-    while True: 
-        plt.plot(WF.logic.receive_data(pin))    # plot recorded signal 
-        plt.show() 
- 
-except KeyboardInterrupt: 
-    pass 
-finally: 
-    # close the device 
-    WF.close() 
- 
-</code> 
-</WRAP></WRAP> 
-<-- 
- 
---> Testing the Pmod ALS: Test_PMOD_ALS.py # 
-<WRAP group><WRAP half column> 
-To test the Pmod ALS, the ambient light intensity is read and displayed continuously. 
-</WRAP><WRAP half column> 
-<code python> 
-# import modules 
-from PMOD import ALS 
-import WaveForms_HAL as WF 
- 
-# define pins 
-ALS.pins.cs = 10 
-ALS.pins.sdo = 9 
-ALS.pins.sck = 8 
- 
-try: 
-    # initialize the interface 
-    WF.initialize() 
-    ALS.initialize(WF) 
- 
-    while True: 
-        # display measurements 
-        print(ALS.receive_percent()) 
- 
-except KeyboardInterrupt: 
-    pass 
-finally: 
-    # close the device 
-    ALS.close() 
-    WF.close() 
- 
-</code> 
-</WRAP></WRAP> 
-<-- 
- 
---> Testing the Pmod BLE: Test_PMOD_BLE.py # 
-<WRAP group><WRAP half column> 
-To test the Pmod BLE, all messages received on Bluetooth are displayed, the string "got it" is sent as a response. 
- 
 To be able to test the Pmod, download the [[https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner|BLE Scanner]] application to your phone. Run the Python script, then start the application. It will ask you to turn on Bluetooth and location. After some time, you should see the Pmod BLE appearing in the scanner. To be able to test the Pmod, download the [[https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner|BLE Scanner]] application to your phone. Run the Python script, then start the application. It will ask you to turn on Bluetooth and location. After some time, you should see the Pmod BLE appearing in the scanner.
  
Line 2262: Line 1462:
 </WRAP> </WRAP>
  
-</WRAP><WRAP half column> +</WRAP><WRAP half column><code python> 
-<code python>+""" To test the Pmod BLE, all messages received on Bluetooth are displayed, the string "ok" is sent as a response. """ 
 + 
 # import modules # import modules
-from PMOD import BLE +import Pmod_BLE as ble 
-import WaveForms_HAL as WF +import WF_SDK as wf # import WaveForms instruments 
 + 
 # define pins # define pins
-BLE.pins.tx = 6 +ble.pins.tx = 4 
-BLE.pins.rx = 5 +ble.pins.rx = 3 
-BLE.pins.rst = 4+ble.pins.rst = 
 +ble.pins.status = 6
  
 +# turn on messages
 +ble.settings.DEBUG = True
 + 
 try: try:
     # initialize the interface     # initialize the interface
-    WF.initialize() +    device_data = wf.device.open() 
-    BLE.initialize(WF+    # check for connection errors 
-    BLE.reboot() +    wf.device.check_error(device_data) 
 +    ble.open() 
 +    ble.reset(rx_mode="uart", tx_mode="uart", reopen=True
 +    ble.reboot() 
 + 
     while True:     while True:
-        # receive data +        # check connection status 
-        data, sys_msg, error = BLE.receive_data() +        if ble.get_status(): 
- +            # receive data 
-        merge data and system messages +            data, sys_msg, error = ble.read(blocking=True, rx_mode="logic", reopen=False
-        buf = "" +            display data and system messages 
-        if len(data) > 0: +            if data != "": 
-            buf = data +                print("data: " + data)  # display it 
-        elif len(sys_msg) > 0: +                ble.write_data("ok", tx_mode="pattern", reopen=False)  # and send response 
-            buf = sys_msg +            elif sys_msg != "": 
- +                print("system: " + sys_msg) 
-        # if the data is valid +            elif error != "": 
-        if len(buf) > 0: +                print("error: " + error)    # display the error 
-            print(buf)  # display it + 
-            BLE.send_data(" got it" # and send response +
- +
-        # if the data isn't valid +
-        elif error != "no error": +
-            print(error)    # display the error +
 except KeyboardInterrupt: except KeyboardInterrupt:
     pass     pass
 finally: finally:
     # close the device     # close the device
-    BLE.reboot() +    ble.close(reset=True
-    BLE.close() +    wf.device.close(device_data
-    WF.close() +</code></WRAP></WRAP>
- +
-</code> +
-</WRAP></WRAP>+
 <-- <--
 +</WRAP>
  
---> Testing the Pmod DA1: Test_PMOD_DA1.py #+<WRAP group> 
 +--> Testing the Pmod ALS ^#
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-Connect the oscilloscope channels of the ADP to the output channels of the Pmod DA1, then run the script. Enter voltage levels for each DAC channel, then compare them with the ones measured by the oscilloscope. Enter an "x" to finish the program+Use the flashlight of a phone to illuminate the sensor while the script is runningUse your hand to cover it to test it in dark
-</WRAP><WRAP half column> +</WRAP><WRAP half column><code python> 
-<code python> +""" To test the Pmod ALS, the ambient light intensity is read and displayed continuously. """
-# import modules +
-from PMOD import DA1 +
-import WaveForms_HAL as WF+
  
-# define used pins 
-DA1.pins.sync = 3 
-DA1.pins.d0 = 2 
-DA1.pins.d1 = 1 
-DA1.pins.sck = 0 
- 
-try: 
-    # initialize the interface 
-    WF.initialize() 
-    DA1.initialize(WF) 
- 
-    # initialize the scope with default settings 
-    WF.scope.initialize() 
- 
-    while True: 
-        # read voltage from keyboard 
-        voltage = input("Set voltage for A1: ") 
-        # check exit condition 
-        if voltage == "x": 
-            break 
-        # set voltage 
-        DA1.output_voltage(DA1.output_channel.A1, float(voltage)) 
-        # display measured voltage levels 
-        print([WF.scope.measure(1), WF.scope.measure(2), WF.scope.measure(3), WF.scope.measure(4)]) 
- 
-        # read voltage from keyboard 
-        voltage = input("Set voltage for B1: ") 
-        # check exit condition 
-        if voltage == "x": 
-            break 
-        # set voltage 
-        DA1.output_voltage(DA1.output_channel.B1, float(voltage)) 
-        # display measured voltage levels 
-        print([WF.scope.measure(1), WF.scope.measure(2), WF.scope.measure(3), WF.scope.measure(4)])         
- 
-        # read voltage from keyboard 
-        voltage = input("Set voltage for A2: ") 
-        # check exit condition 
-        if voltage == "x": 
-            break 
-        # set voltage 
-        DA1.output_voltage(DA1.output_channel.A2, float(voltage)) 
-        # display measured voltage levels 
-        print([WF.scope.measure(1), WF.scope.measure(2), WF.scope.measure(3), WF.scope.measure(4)]) 
- 
-        # read voltage from keyboard 
-        voltage = input("Set voltage for B2: ") 
-        # check exit condition 
-        if voltage == "x": 
-            break 
-        # set voltage 
-        DA1.output_voltage(DA1.output_channel.B2, float(voltage)) 
-        # display measured voltage levels 
-        print([WF.scope.measure(1), WF.scope.measure(2), WF.scope.measure(3), WF.scope.measure(4)]) 
- 
-except KeyboardInterrupt: 
-    pass 
-finally: 
-    # close the device 
-    DA1.close() 
-    WF.close() 
- 
-</code> 
-</WRAP></WRAP> 
-<-- 
- 
---> Testing the Pmod KYPD: Test_PMOD_KYPD.py # 
-<WRAP group><WRAP half column> 
-To test the keypad, read and display the pressed keys continuously. 
-</WRAP><WRAP half column> 
-<code python> 
 # import modules # import modules
-from PMOD import KYPD +import Pmod_ALS as als 
-import WaveForms_HAL as WF+import WF_SDK as wf # import WaveForms instruments 
 +from time import sleep
  
 # define pins # define pins
-KYPD.pins.rows [7, 6, 5, 4] +als.pins.cs 8 
-KYPD.pins.columns [3, 2, 1, 0]+als.pins.sdo 
 +als.pins.sck = 10
  
 try: try:
     # initialize the interface     # initialize the interface
-    WF.initialize() +    device_data = wf.device.open() 
-    KYPD.initialize(WF)+    # check for connection errors 
 +    wf.device.check_error(device_data
 +    als.open()
  
     while True:     while True:
-        # display pressed keys +        # display measurements 
-        print(KYPD.receive_data())+        light = als.read_percent(rx_mode="static", reopen=True) 
 +        print("static: " + str(light) + "%"
 +        light = als.read_percent(rx_mode="spi", reopen=True) 
 +        print("spi: " + str(light) + "%"
 +        sleep(0.5)
  
 except KeyboardInterrupt: except KeyboardInterrupt:
Line 2412: Line 1547:
 finally: finally:
     # close the device     # close the device
-    KYPD.close() +    als.close(reset=True
-    WF.close() +    wf.device.close(device_data
- +</code></WRAP></WRAP>
-</code> +
-</WRAP></WRAP>+
 <-- <--
- 
---> Testing the Pmod OD1: Test_PMOD_OD1.py # 
-<WRAP group><WRAP half column> 
-To test the open drain MOSFETs, connect the cathode (shorter lead) of some LEDs to the MOSFET drains. Connect one and of 470Ω resistors to the anodes (longer lead) of the LEDs. Connect the other lead of the resistors to the power supply output (red cable) on ADP. 
- 
-The script turns on and off every LED one after the other, then repeats this step, but with the toggling function. When finished, enter duty cycles for every PWM signal commanding the MOSFETs to set the brightness of the LEDs. Finish the script by entering "x" instead of a duty cycle. 
-</WRAP><WRAP half column> 
-<code python> 
-# import modules 
-from PMOD import OD1 
-import WaveForms_HAL as WF 
-import time 
- 
-# define used pins 
-OD1.pins.g1 = 3 
-OD1.pins.g2 = 2 
-OD1.pins.g3 = 1 
-OD1.pins.g4 = 0 
- 
-# define signal frequency 
-frequency = 1 
- 
-# define PWM type 
-static_pwm = True 
- 
-# require static testing 
-static_required = False 
- 
-# define delay after operations 
-delay = 1   # in seconds 
- 
-try: 
-    # initialize the interface 
-    WF.initialize() 
-    OD1.initialize(WF) 
- 
-    if static_required: 
-        # try the on/off functions 
-        for index in range(1, 5): 
-            OD1.on(index)   # turn on MOSFET 
-            print("on" # display message 
-            time.sleep(delay)   # wait 
-            OD1.off(index)  # turn off MOSFET 
-            print("off" # display message 
-            time.sleep(delay)   # wait 
- 
-        # try the toggling function 
-        for index in range(1, 5): 
-            OD1.switch(index)   # toggle MOSFET 
-            print("toggle" # display message 
-            time.sleep(delay)   # wait 
-            OD1.switch(index)   # toggle MOSFET 
-            print("toggle" # display message 
-            time.sleep(delay)   # wait 
- 
-    while True: 
-        # read duty cycles from keyboard 
-        duty = input("First MOSFET duty cycle: ") 
-        # check exit condition 
-        if duty == "x": 
-            break 
-        # test the pwm drive functions 
-        OD1.drive(1, frequency, int(duty), static=static_pwm) 
- 
-        duty = input("Second MOSFET duty cycle: ") 
-        if duty == "x": 
-            break 
-        OD1.drive(2, frequency, int(duty), static=static_pwm) 
- 
-        duty = input("Third MOSFET duty cycle: ") 
-        if duty == "x": 
-            break 
-        OD1.drive(3, frequency, int(duty), static=static_pwm) 
- 
-        duty = input("Fourth MOSFET duty cycle: ") 
-        if duty == "x": 
-            break 
-        OD1.drive(4, frequency, int(duty), static=static_pwm) 
- 
-except KeyboardInterrupt: 
-    pass 
-finally: 
-    # turn off everything 
-    OD1.off(1) 
-    OD1.off(2) 
-    OD1.off(3) 
-    OD1.off(4) 
- 
-    # close the device 
-    OD1.close() 
-    WF.close() 
- 
-</code> 
-</WRAP></WRAP> 
-<-- 
- 
 </WRAP> </WRAP>
 ---- ----
Line 2520: Line 1557:
 <WRAP group> <WRAP group>
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-To build an Android application, the [[https://appinventor.mit.edu/|MIT App Inventor]] web application will be used. Open the site, create a new user, then start a new project. First, create the user interface of the application by drag and dropping user interface elements onto the virtual phone. Use the companion app on your real phone to see how the user interface will look. Alternatively, you can download and import the already created {{ :test-and-measurement:analog-discovery-pro-3x50:smart_lamp_project.zip |project file}}, or just download and install on your phone the final {{ :test-and-measurement:analog-discovery-pro-3x50:smart_lamp_app.zip |application}}.+To build an Android application, the [[https://appinventor.mit.edu/|MIT App Inventor]] web tool will be used. Open the site, create a new user, then start a new project. First, create the user interface of the application by drag and dropping user interface elements onto the virtual phone. Use the companion app on your real phone to see how the user interface will look. Alternatively, you can download and import the already created {{ :test-and-measurement:analog-discovery-pro-3x50:digilent_lamp_controller_project.zip |project file}}, or just download and install on your phone the final {{ :test-and-measurement:analog-discovery-pro-3x50:digilent_lamp_controller_apk.zip |application}}. 
 + 
 +--> QR Code for the Application # 
 +<WRAP group>{{ :test-and-measurement:analog-discovery-pro-3x50:smart-lamp-controller-apk.png?700 |}}</WRAP> 
 +<--
  
 For this application, a switch, three sliders, and several labels are needed. Use the //Horizontal Arrangement// and //Vertical Arrangement// blocks from the //Layout// menu to arrange everything on the screen. For this application, a switch, three sliders, and several labels are needed. Use the //Horizontal Arrangement// and //Vertical Arrangement// blocks from the //Layout// menu to arrange everything on the screen.
Line 2531: Line 1572:
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
 When you are finished with the user interface, enter Block view. Here use the puzzle pieces to create the logic backbone of your application. Define what happens when the user touches a slider/switch/label, when the Bluetooth module connects/disconnects/receives a message. Define what data do you want to send and how to decode the received information. If you have never created an application before, this is a great way to start. When you are finished with the user interface, enter Block view. Here use the puzzle pieces to create the logic backbone of your application. Define what happens when the user touches a slider/switch/label, when the Bluetooth module connects/disconnects/receives a message. Define what data do you want to send and how to decode the received information. If you have never created an application before, this is a great way to start.
-<WRAP center round tip>+</WRAP><WRAP half column> 
 +{{ :test-and-measurement:analog-discovery-pro-3x50:app_builder_back.png?600 |}} 
 +</WRAP></WRAP> 
 + 
 +<WRAP group> 
 +--> Tips and Tricks # 
 +<WRAP center round tip 80%>
 Use comments on the pieces (small blue circles with a question mark) to make your "code" easy to understand. Use comments on the pieces (small blue circles with a question mark) to make your "code" easy to understand.
 </WRAP> </WRAP>
-<WRAP center round tip>+<WRAP center round tip 80%>
 In this editor, every separate puzzle piece runs as an interrupt. Use this to your advantage. In this editor, every separate puzzle piece runs as an interrupt. Use this to your advantage.
 </WRAP> </WRAP>
-<WRAP center round tip>+<WRAP center round tip 80%>
 Hiding a component and invisible components can be useful. You can use an invisible error message, which is made visible only if an error appears, then hide it again using a timer interrupt, to display connection problems - this can be extremely useful during debugging. Hiding a component and invisible components can be useful. You can use an invisible error message, which is made visible only if an error appears, then hide it again using a timer interrupt, to display connection problems - this can be extremely useful during debugging.
 </WRAP> </WRAP>
-<WRAP center round tip>+<WRAP center round tip 80%>
 To easily access the BLE device, service and characteristic you need, use the **MAC address**, **Service UUID** and **Characteristic UUID** obtained previously. If you don't have those, you can find the addresses by following this step: [[test-and-measurement:analog-discovery-pro-3x50:smart-lamp#testing_the_modules|Testing the Pmod BLE]]. To easily access the BLE device, service and characteristic you need, use the **MAC address**, **Service UUID** and **Characteristic UUID** obtained previously. If you don't have those, you can find the addresses by following this step: [[test-and-measurement:analog-discovery-pro-3x50:smart-lamp#testing_the_modules|Testing the Pmod BLE]].
 </WRAP> </WRAP>
-</WRAP><WRAP half column> +<-- 
-{{ :test-and-measurement:analog-discovery-pro-3x50:app_builder_back.png?600 |}} +</WRAP>
-</WRAP></WRAP>+
  
 <WRAP group> <WRAP group>
Line 2557: Line 1603:
 === Connecting the PMODs === === Connecting the PMODs ===
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-Connect the Pmod ALSthe Pmod BLEthe Pmod DA1, and one channel of the Pmod OD1 to the Analog Discovery Pro.+Connect the Pmod ALS and the Pmod BLE to the digital lines of the Analog Discovery Pro. Note the connections as you will have to define them in the code.
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:adp_pmods_bb.png?600 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:pmods_to_adp3450_v2.png?400 |}}
 </WRAP></WRAP> </WRAP></WRAP>
  
-=== Building the PWM Generator === +=== Building the LED Driver === 
-<WRAP group><WRAP half column+<WRAP group> 
-Connect three output channels of the Pmod DA1 to the inverting input of the comparators realized with the OP484 operational amplifierTo the non-inverting inputconnect the second channel of the waveform generatorThis circuit will create the PWM signals from the numerical data: the waveform generator outputs a sawtooth signal with 0.5V amplitude and 0.5V offset and this signal is compared to the voltage levels provided by the DAC so the rail-to-rail comparator generates logic levels according to the comparison result. The duty cycle of the resulting signal can be easily set0V on the DAC means 100% duty cycle, while 1V on the DAC means 0% duty cycle.+To have a more linear control of brightness, not the voltage falling on a LED, but the current through it must be controlledAs the ADP3450 doesn't have current suppliesa voltage controlled current sink has to be built for each color of the RGB LEDThe control voltages for these current sinks can be provided by three digital lines, by generating PWM signals and connecting these signals to low-pass filtersIn the circuit below, the current through the LED is proportional to the duty cycle of the control signal: $I_{LED}=\frac{D}{100}\frac{V_{CC}}{R_{SET}}$. Don't forget to calculate the power dissipation on the set resistor and choose the resistor accordingly!
  
-Connect the outputs of the comparators to the remaining MOSFET gates on the Pmod OD1+You can use the quad op-amp to implement all three current sinks
-</WRAP><WRAP half column+</WRAP> 
-{{ :test-and-measurement:analog-discovery-pro-3x50:pwm_generator_bb.png?600 |}} +<WRAP group><html
-</WRAP></WRAP>+<iframe width="100%" height="600" src="https://www.multisim.com/content/HQu9BSDScJjweQxMkYzrEe/led-driver/open"></iframe> 
 +</html></WRAP>
  
 +=== Connecting the Charger Circuit ===
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
-To test the PWM generator circuit, you can connect an oscilloscope channel to the output of a comparator, then use WaveForms to generate the signal: first provide power to the Pmods by enabling the Supplies instrument, then generate the reference signal (sawtooth signal with 500mV offset and 500mV amplitude) on the second Wavegen channel. Use the SPI Master tool in the Protocol instrument to command the DAC and the Scope to display the generated signal. To generate the control voltages, use the command 0x03 to select channel A, or 0x07 to select channel B (the data line selects the DAC chip), then send the data word (a number between 0 - 0V and 77 - 0.99V). The duty cycle is set by the sent data word. +Connect the charger circuit to one USB port in the back of the Analog Discovery Pro, then to the battery and the RGB LEDs.
-</WRAP><WRAP half column> +
-{{ :test-and-measurement:analog-discovery-pro-3x50:pwm_generator_test.png?600 |}} +
-</WRAP></WRAP>+
  
-=== Building the Charger Circuit === +To be able to measure the voltage of the battery cell, connect the first oscilloscope channel to the negative lead of the battery and the second channel to the positive lead. As the scope channels have a common reference, the difference between the two measurements will give the battery voltage. To check if the charger is connected, or not, connect one scope channel to the input of the charger.
-<WRAP group><WRAP half column> +
-Use the Micro-USB plug to provide power to the battery charger. You can connect the charger to the back panel of the ADP. The charger consists of a programmable current sink, realized with the LT3092. The reference voltage will be provided by the first waveform generator channel of the ADP. The current sink can charge up the Li-Po cell until it reaches 4.2V. From here the cell would need a constant voltage source with decreasing current to get the topping charge, but to keep the circuit simple, this part won't be implemented. +
- +
-To prevent the negative lead of the battery from becoming floating, the first MOSFET is used in the Pmod OD1 to couple it to the ground, when the charger isn't plugged in. A charge signaling LED is also connected to the ADP to provide feedback. +
- +
-To be able to measure the voltage of the battery cell, connect the first oscilloscope channel to the negative lead of the battery and the second channel to the positive lead. As the scope channels have a common reference, the difference between the two measurements will give the battery voltage.+
 </WRAP><WRAP half column> </WRAP><WRAP half column>
-{{ :test-and-measurement:analog-discovery-pro-3x50:charger_circuit_bb.png?600 |}}+{{ :test-and-measurement:analog-discovery-pro-3x50:charger_to_adp3450.png?600 |}}
 </WRAP></WRAP> </WRAP></WRAP>
 +/*
 === Connecting the RGB LED === === Connecting the RGB LED ===
 <WRAP group><WRAP half column> <WRAP group><WRAP half column>
Line 2593: Line 1632:
 </WRAP><WRAP half column> </WRAP><WRAP half column>
 {{ :test-and-measurement:analog-discovery-pro-3x50:rgb_led_bb.png?600 |}} {{ :test-and-measurement:analog-discovery-pro-3x50:rgb_led_bb.png?600 |}}
-</WRAP></WRAP>+</WRAP></WRAP>*/
 </WRAP> </WRAP>
 ---- ----
Line 2599: Line 1638:
 ===== Software Setup ===== ===== Software Setup =====
 <WRAP group> <WRAP group>
-In the following, the structure of the main program file will be detailed. You can follow this guide to write your own control program based on the instructions given here, or you can download the source code {{ :test-and-measurement:analog-discovery-pro-3x50:lamp_controller.zip |here}}.+In the following, the structure of the main program file will be detailed. You can follow this guide to write your own control program based on the instructions given here, or you can download the script {{ :test-and-measurement:analog-discovery-pro-3x50:lamp_controller.zip |here}}.
  
-=== Importing the Modules and Defining Connections === +<WRAP group>
-<WRAP group><WRAP half column>+
 To be able to use all the previously created modules, you must import them into your script. To be able to use all the previously created modules, you must import them into your script.
  
 To make your code more readable and easier to debug, it is good practice to name your connections (so you don't have to keep in mind which digital, or analog channel is used for what). To make your code more readable and easier to debug, it is good practice to name your connections (so you don't have to keep in mind which digital, or analog channel is used for what).
-</WRAP><WRAP half column>+ 
 +Also define every important constant at the beginning of your script. If you initialize these values at the start of your code, it will be easier to modify them during tuning the finished project. 
 +</WRAP> 
 + 
 +<WRAP group> 
 +--> Importing the Modules, Defining Connections and Constants #
 <code python> <code python>
 +""" Control an RGB LED with the ADP3450 """
 +
 # import modules # import modules
-from PMOD import DA1, BLE, ALS, OD1 +import Pmod_BLE as ble 
-import WaveForms_HAL as WF +import Pmod_ALS as als 
-import time +import WF_SDK as wf 
- +from time import time
-"""-------------------------------------------------------------------"""+
  
 # define connections # define connections
- 
-# PMOD DA1 pins 
-DA1.pins.sync = 3 
-DA1.pins.d0 = 2 
-DA1.pins.d1 = 1 
-DA1.pins.sck = 0 
- 
-# Pmod OD1 pins 
-OD1.pins.g1 = 7 
- 
 # PMOD BLE pins # PMOD BLE pins
-BLE.pins.rst = 4 +ble.pins.rx = 3 
-BLE.pins.rx = 5 +ble.pins.tx = 4 
-BLE.pins.tx = 6 +ble.pins.rst = 5 
 +ble.pins.status = 6
 # PMOD ALS pins # PMOD ALS pins
-ALS.pins.cs = 10 +als.pins.cs = 8 
-ALS.pins.sdo = 9 +als.pins.sdo = 9 
-ALS.pins.sck = +als.pins.sck = 10
- +
-# digital I/O +
-signal_LED = 11 +
 # scope channels # scope channels
-battery_neg_ch = 1 +SC_BAT_P = 1 
-battery_pos_ch = 2+SC_BAT_N = 2 
 +SC_CHARGE = 4 
 +# LED colors 
 +LED_R = 0 
 +LED_G = 1 
 +LED_B = 2
  
-wavegen channels +other parameters 
-current_reference 1 +scope_average 10  # how many measurements to average with the scope 
-pwm_reference +light_average 10  # how many measurements to average with the light sensor 
- +led_pwm_frequency = 1e03    in Hz 
-lamp color references +out_data_update 10    # output data update time [s] 
-lamp_red DA1.output_channel.A1 +ble.settings.DEBUG True   # turn on messages from Pmod BLE 
-lamp_green DA1.output_channel.B1 +als.settings.DEBUG = True   # turn on messages from Pmod ALS 
-lamp_blue DA1.output_channel.A2+DEBUG True                # turn on messages
  
-define low side switch (OD1 channel) +encoding prefixes 
-low_switch 1+pre_red 0b11 
 +pre_green = 0b10 
 +pre_blue = 0b01 
 +pre_bat = 0b11 
 +pre_charge = 0b10 
 +pre_light = 0b01
  
 +class flags:
 +    red = 0
 +    green = 0
 +    blue = 0
 +    last_red = -1
 +    last_green = -1
 +    last_blue = -1
 +    start_time = 0
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP>
  
-=== Global Variables and Auxiliary Functions === +<WRAP group> 
-<WRAP group><WRAP half column+Some auxiliary functions might be needed as well, which will be used later in the scriptUse these functions to make the script easier to read. 
-Define the global parameters of your projectIf you initialize these variables at the start of your code, it will be easier to modify them during tuning the finished project.+</WRAP>
  
-Some auxiliary functions might be needed as well, which will be used later in the script. +<WRAP group> 
-</WRAP><WRAP half column>+--Auxiliary Functions #
 <code python> <code python>
-# other parameters +def rgb_led(red, green, blue, device_data): 
-light_referesh = 2  # delay between refreshing the light intensity (in seconds+    """ 
-scope_average = 10  # how many measurements to average with the scope +        define the LED color in precentage 
-light_average = 10  # how many measurements to average with the light sensor +    """ 
-battery_charged = 4.2   # battery charge level in volts +    wf.pattern.generate(device_data, LED_R, wf.pattern.function.pulse, led_pwm_frequency, duty_cycle=red) 
-battery_discharged = 3.7    # battery discharge level in volts +    wf.pattern.generate(device_data, LED_G, wf.pattern.function.pulse, led_pwm_frequency, duty_cycle=green) 
-max_charge_current 0.   # maximum charge current of the battery (in Amperes)+    wf.pattern.generate(device_data, LED_B, wf.pattern.function.pulse, led_pwm_frequency, duty_cycle=blue) 
 +    return
  
 """-------------------------------------------------------------------""" """-------------------------------------------------------------------"""
  
 +def decode(data):
 +    """
 +        decode incoming Bluetooth data
 +    """
 +    # convert to list
 +    for character in list(data):
 +        # convert character to integer
 +        character = ord(character)
 +        # check prefixes and extract content
 +        if character & 0xC0 == pre_red << 6:
 +            flags.red = round((character & 0x3F) / 0x3F * 100)
 +        elif character & 0xC0 == pre_green << 6:
 +            flags.green = round((character & 0x3F) / 0x3F * 100)
 +        elif character & 0xC0 == pre_blue << 6:
 +            flags.blue = round((character & 0x3F) / 0x3F * 100)
 +        else:
 +            pass
 +    return
  
-def scale(value, source, destination): +"""-------------------------------------------------------------------"""
-    # scale a number from a range to another +
-    return ((value source[0]) / (source[1] source[0])) * (destination[1] destination[0]) + destination[0]+
  
 +def encode(data, prefix, lim_max, lim_min=0):
 +    """
 +        encode real numbers between lim_min and lim_max
 +    """
 +    try:
 +        # clamp between min and max limits
 +        data = max(min(data, lim_max), lim_min)
 +        # map between 0b000000 and 0b111111
 +        data = (data - lim_min) / (lim_max - lim_min) * 0x3F
 +        # append prefix
 +        data = (int(data) & 0x3F) | (prefix << 6)
 +        return data
 +    except:
 +        return 0
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP>
  
 +<WRAP group>
 The "body" of the script is inserted in a try-except structure. This structure runs the code sequence in the "try" block, and if an error (exception) occurs, handles it as defined in the "except" block. In this case the "except" block is empty (the script just finishes when the Ctrl+C combination is pressed), but it is followed by a "finally" block, which runs only once, when the try-except structure is exited. Here, the cleanup procedure can be executed, which will be discussed later. The "body" of the script is inserted in a try-except structure. This structure runs the code sequence in the "try" block, and if an error (exception) occurs, handles it as defined in the "except" block. In this case the "except" block is empty (the script just finishes when the Ctrl+C combination is pressed), but it is followed by a "finally" block, which runs only once, when the try-except structure is exited. Here, the cleanup procedure can be executed, which will be discussed later.
  
-=== Initialization === +To be able to use the instruments, you must initialize the HAL created previously, as well as the modules controlling the PMODs. After everything is initialized, turn off the lamp. 
-<WRAP group><WRAP half column> +</WRAP> 
-To be able to use the instruments, you must initialize the HAL created previously, as well as the modules controlling the PMODs. After everything is initialized, start generating the reference signals on the Wavegen channels, but turn off the lamp and the signaling LED. You can also make the battery ground floating, by turning off the MOSFET used as a low-side switch+ 
-</WRAP><WRAP half column>+<WRAP group> 
 +--> Initialization #
 <code python> <code python>
 +try:
     # initialize the interface     # initialize the interface
-    WF.initialize() +    device_data = wf.device.open() 
- +    # check for connection errors 
-    # initialize the scope with default settings +    wf.device.check_error(device_data
-    WF.scope.initialize() +    if DEBUG: 
- +        print(device_data.name + " connected") 
-    # initialize the PMODs +    # start the power supplies 
-    DA1.initialize(WF+    supplies_data = wf.supplies.data() 
-    OD1.initialize(WF) +    supplies_data.master_state = True 
-    ALS.initialize(WF) +    supplies_data.state = True 
-    BLE.initialize(WF) +    supplies_data.voltage = 3.3 
-    BLE.reboot() +    wf.supplies.switch(device_data, supplies_data
- +    if DEBUG: 
-    """---------------------------------""" +        print("power supplies started") 
- +    # initialize the light sensor 
-    # start generating the reference signals +    als.open() 
-    WF.wavegen.generate(current_reference, WF.wavegen.type.dc, 0+    # initialize the Bluetooth module 
-    WF.wavegen.generate(pwm_reference, WF.wavegen.type.ramp.up, +    ble.open() 
-                        0.5, frequency=1e03, offset=0.5, symmetry=100) +    ble.reboot(
- +    
-    """---------------------------------""" +
     # turn off the lamp     # turn off the lamp
-    DA1.output_voltage(lamp_red1) +    rgb_led(000device_data
-    DA1.output_voltage(lamp_green1) +    if DEBUG: 
-    DA1.output_voltage(lamp_blue1) +        print("entering main loop")
- +
-    # turn off the switch and the LED +
-    WF.digital.set_output(signal_LED) +
-    WF.digital.write(signal_LED, False) +
-    OD1.off(low_switch) +
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP>
  
-=== Endless Loop === +<WRAP group> 
-<WRAP group><WRAP half column+The main part of the script is run in an endless loop, which can be exited only by a keyboard interrupt. The main loop first handles the received data: the script decodes itthen sets the color and brightness of the lamp accordingly. The next section checks if the internal timer signals the end of the update periodreads the light intensity from the Pmod ALSthe battery voltage and the state of the charger (connected/disconnected), then sends these information to the application
-The main part of the script is run in an endless loop, which can be exited only by a keyboard interrupt. Howeverthere are some stateswhich must be kept unchanged between iterationsso certain flags and variables must be initialized before the loop. +</WRAP>
-</WRAP><WRAP half column> +
-<code python> +
-    # variables to remember states +
-    start_time = time.time() +
-    lamp_on = False +
-    off_flag = True +
-    charger_connected = False +
-</code> +
-</WRAP></WRAP>+
  
-<WRAP group><WRAP half column+<WRAP group> 
-All other variables are temporal, they change in every iterations, so they can be defined in the loop. +--Main Loop: Receiving Data #
-</WRAP><WRAP half column>+
 <code python> <code python>
-        # temporal variables +    while True: 
-        BT_flag = False +        if ble.get_status(): 
-        battery_negative = +            # process the data and set lamp color 
-        battery_positive 0 +            data, sys_msg, error = ble.read(blocking=True, rx_mode="logic", reopen=False) 
-        light 0+            if len(data) > 0: 
 +                # decode incoming data 
 +                decode(data) 
 +                # set the color 
 +                if flags.red !flags.last_red: 
 +                    flags.last_red flags.red 
 +                    rgb_led(flags.red, flags.green, flags.blue, device_data) 
 +                    if DEBUG: 
 +                        print("red: " + str(flags.red) + "%"
 +                elif flags.green != flags.last_green: 
 +                    flags.last_green = flags.green 
 +                    rgb_led(flags.red, flags.green, flags.blue, device_data) 
 +                    if DEBUG: 
 +                        print("green: " + str(flags.green) + "%"
 +                elif flags.last_blue != flags.blue: 
 +                    flags.last_blue = flags.blue 
 +                    rgb_led(flags.red, flags.green, flags.blue, device_data) 
 +                    if DEBUG: 
 +                        print("blue: " + str(flags.blue) + "%")
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP>
  
-=== Receive Data on Bluetooth Low Energy === +<WRAP group> 
-<WRAP group><WRAP half column+--Main Loop: Sending Data #
-As a first step, read the messages from the PMOD BLE and save the connection status (also turn off the lamp if the module is disconnected). If the data is received without error, convert the received bytes to voltages, then set the lamp color, by setting the output values of the DAC channels. +
-</WRAP><WRAP half column>+
 <code python> <code python>
-        # read data from the Bluetooth module +        if ble.get_status(): 
-        data, sys_msg, error BLE.receive_data() +            check timing 
-        data list(data)+            duration time() - flags.start_time 
 +            if duration >= out_data_update: 
 +                # save current time 
 +                flags.start_time = time() 
 +                # measure the light intensity 
 +                light 
 +                for _ in range(light_average)
 +                    light += als.read_percent(rx_mode="static", reopen=False) 
 +                light /= light_average
  
-        turn the lamp on/off +                encode and send the light intensity 
-        if sys_msg.startswith("%CONNECT"): +                light = encode(light, pre_light, 100
-            lamp_on = True +                ble.write_data(light, tx_mode="pattern", reopen=False)
-        elif sys_msg == "%DISCONNECT%"+
-            lamp_on = False+
  
-        process the data +                read battery voltage 
-        if error == "no error" and len(data>= 3+                batt_n 
-            BT_flag True +                batt_p 
-            data [ord(element) for element in data] +                for _ in range(scope_average): 
-            duty_red = scale(data[-3], [1, 255], [1, 0]+                    batt_n +wf.scope.measure(device_data, SC_BAT_N) 
-            duty_green scale(data[-2][1, 255], [1, 0]+                batt_n /scope_average 
-            duty_blue scale(data[-1], [1, 255], [1, 0])+                for in range(scope_average): 
 +                    batt_p +wf.scope.measure(device_dataSC_BAT_P
 +                batt_p /scope_average 
 +                battery_voltage = batt_p batt_n
  
-        set lamp color according to received data +                encode and send voltage 
-        if lamp_on: +                battery_voltage encode(battery_voltagepre_bat5
-            if BT_flag: +                ble.write_data(battery_voltagetx_mode="pattern"reopen=False)
-                BT_flag False +
-                off_flag = False +
-                DA1.output_voltage(lamp_redduty_red) +
-                DA1.output_voltage(lamp_greenduty_green+
-                DA1.output_voltage(lamp_blueduty_blue) +
-        else: +
-            if not off_flag: +
-                off_flag True +
-                DA1.output_voltage(lamp_red1) +
-                DA1.output_voltage(lamp_green, 1) +
-                DA1.output_voltage(lamp_blue, 1)+
  
 +                # read charger state
 +                charger_voltage = 0
 +                for _ in range(scope_average):
 +                    charger_voltage += wf.scope.measure(device_data, SC_CHARGE)
 +                charger_voltage /= scope_average
 +
 +                # encode and send voltage
 +                charger_voltage = encode(charger_voltage, pre_charge, 5)
 +                ble.write_data(charger_voltage, tx_mode="pattern", reopen=False)
 +        else:
 +            rgb_led(0, 0, 0, device_data)
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP>
  
-=== Measuring the Light Intensity === +<WRAP group> 
-<WRAP group><WRAP half column+When the script is finishedturn off the lamp, then close and reset the used instruments and disconnect from the device
-Light intensity is measured only after a predefined time interval. When it is needed, the PMOD ALS is used to measure the light intensity, then the result is encoded to a byte and sent using the Pmod BLE+</WRAP> 
-</WRAP><WRAP half column>+ 
 +<WRAP group> 
 +--Cleanup #
 <code python> <code python>
-        # if the light intensity has to be measured +except KeyboardInterrupt
-        if time.time() - start_time > light_referesh+    exit on Ctrl+C 
-            reinitialize the SPI interface +    if DEBUG
-            ALS.initialize(WF) +        print("keyboard interrupt detected")
- +
-            # measure the light intensity +
-            for _ in range(light_average)+
-                light += ALS.receive_percent() +
-            light /= light_average +
- +
-            # encode the light intensity +
-            encoded_light = round(light * 2.55) & 0xFF +
- +
-            # reinitialize the UART interface +
-            BLE.initialize(WF) +
- +
-            # send light intensity +
-            BLE.send_data(encoded_light) +
- +
-            start_time = time.time()+
  
 +finally:
 +    if DEBUG:
 +        print("closing used instruments")
 +    # turn off the lamp
 +    rgb_led(0, 0, 0, device_data)
 +    # close PMODs
 +    ble.close(True)
 +    als.close(True)
 +    # stop and reset the power supplies
 +    supplies_data = wf.supplies.data()
 +    supplies_data.master_state = False
 +    supplies_data.state = False
 +    supplies_data.voltage = 0
 +    wf.supplies.switch(device_data, supplies_data)
 +    wf.supplies.close(device_data)
 +    if DEBUG:
 +        print("power supplies stopped")
 +    # close device
 +    wf.device.close(device_data)
 +    if DEBUG:
 +        print("script stopped")
 </code> </code>
-</WRAP></WRAP>+<-- 
 +</WRAP> 
 +</WRAP> 
 +----
  
-=== Charging the Battery === +===== Setting Up the Analog Discovery Pro 3450 (Linux Mode) ===== 
-<WRAP group><WRAP half column+<WRAP group> 
-In every iteration of the main loop, measure the battery voltage. Also check, whether the charger is connected, or not and turn on the low-side switch, if the charger is disconnected, to ground the battery properly, but turn off the switch, when the charger is connected, to prevent any overvoltage on the battery cellSet the charging current and turn the signaling LED on, or off according to the state of the charger circuit. +To be able to run the script without a PCyou will have to boot up the Analog Discovery Pro in Linux ModeFollow this guide to guide you through the boot process and to connect to the device with a terminal emulator[[test-and-measurement:analog-discovery-pro-3x50:linux-mode|]].
-</WRAP><WRAP half column> +
-<code python> +
-        # read battery voltage +
-        for _ in range(scope_average): +
-            battery_negative += WF.scope.measure(battery_neg_ch) +
-        battery_negative /= scope_average +
-        for _ in range(scope_average): +
-            battery_positive += WF.scope.measure(battery_pos_ch) +
-        battery_positive /= scope_average +
-        battery_voltage = battery_positive - battery_negative+
  
-        # decide if the charger is connected or not +The next step is to connect the ADP3450 to the internetFollow this guide for the detailed steps[[test-and-measurement:analog-discovery-pro-3x50:connect-to-the-internet|]].
-        if battery_positive >= 4.3: +
-            charger_connected = True +
-            OD1.off(low_switch) +
-        else: +
-            charger_connected = False +
-            OD1.on(low_switch)+
  
-        # calculate charge current reference +Python 3 is already installed on the devicebut there are some additional packageswhich you will have to installFirst install the Python package installer (pipwith the following command (use SSH to send commands to the ADP)
-        charge_current = max_charge_current * scale(battery_voltage[battery_dischargedbattery_charged], [0.5, 1]) +<code>sudo apt install python3-pip</code>
-        charge_current_ref = scale(charge_current, [0, max_charge_current], [0, 1])+
  
-        # start/stop charging +Don't forget to install the HAL created for the WaveForms SDKYou can copy the necessary files in the same directory where the project files areor you can use the following command to install it from GitHub: 
-        if charger_connected and battery_voltage < battery_charged: +<code>sudo pip3 install git+https://github.com/Digilent/WaveForms-SDK-Getting-Started-PY#egg=WF_SDK</code>
-            WF.wavegen.generate(current_referenceWF.wavegen.type.dc, charge_current_ref) +
-            WF.digital.write(signal_LED, True) +
-        else: +
-            WF.wavegen.generate(current_reference, WF.wavegen.type.dc, 0) +
-            WF.digital.write(signal_LED, False)+
  
 +Run the script on boot, by entering these commands in the terminal:
 +<code>
 +sudo su
 +cd /etc/systemd/system
 +echo -n "" > lamp.service
 +nano lamp.service
 </code> </code>
-</WRAP></WRAP>+In the text editor, enter this snippet as the file's content (don't forget to change the path to the file): 
 +<code> 
 +[Unit] 
 +Description=Smart Lamp Controller
  
-=== Cleanup === +[Service] 
-<WRAP group><WRAP half column> +ExecStart=nohup python3 /home/digilent/Smart-Lamp-Controller/Python/Lamp_Controller.py &
-When the script is finished, turn off the lamp and the charger, then close reset the used instruments and disconnect from the device. +
-</WRAP><WRAP half column> +
-<code python> +
-    # turn off the lamp +
-    DA1.output_voltage(lamp_red, 1) +
-    DA1.output_voltage(lamp_green, 1) +
-    DA1.output_voltage(lamp_blue, 1) +
- +
-    # turn off the LED and the switch +
-    WF.digital.write(signal_LED, False) +
-    OD1.off(low_switch) +
- +
-    # close PMODs +
-    ALS.close() +
-    BLE.close() +
-    DA1.close() +
-    OD1.close() +
- +
-    # close device +
-    WF.close()+
  
 +[Install]
 +WantedBy=multi-user.target
 +</code>
 +Save the file with Ctrl+O and exit with Ctrl+X. Enable and try the service by:
 +<code>
 +systemctl start lamp
 +systemctl enable lamp
 +reboot
 </code> </code>
-</WRAP></WRAP> 
- 
 </WRAP> </WRAP>
 ---- ----
Line 2891: Line 1968:
  
 If you want to modify the parameters of the script, stop it with Ctrl+C, modify the parameters, then restart the script. Averaging more measurements leads to a more stable result, with a slower update time. If you want to modify the parameters of the script, stop it with Ctrl+C, modify the parameters, then restart the script. Averaging more measurements leads to a more stable result, with a slower update time.
- 
-Take care not to overcharge, or over-discharge the battery, or overload the power supply. 
 </WRAP> </WRAP>
 ---- ----
Line 2900: Line 1975:
 For more information on WaveForms SDK, see its [[software:waveforms:waveforms-sdk:start|Resource Center]]. For more information on WaveForms SDK, see its [[software:waveforms:waveforms-sdk:start|Resource Center]].
  
-For technical support, please visit the [[https://forum.digilentinc.com/forum/8-test-and-measurement/|Test and Measurement]] section of the Digilent Forums.+For technical support, please visit the [[https://forum.digilent.com/forum/8-test-and-measurement/|Test and Measurement]] section of the Digilent Forums.
 </WRAP> </WRAP>