# Humpback DDS MQTT-controlled 4-channel DDS signal generator using Urukul, Humpback and STM32 NUCLEO. - [Continuous Integration](https://nixbld.m-labs.hk/job/mcu/humpback-dds/humpback-dds) - [Download latest firmware build](https://nixbld.m-labs.hk/job/mcu/humpback-dds/humpback-dds/latest/download-by-type/file/binary-dist) ## Nix commands Humpback-DDS firmware is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``). Once you have Flakes enabled, you can use ``nix build`` to build the firmware. Alternatively, you can develop and build it within Nix shell: ```shell nix develop python fpga/fpga_config.py cargo build --release ``` **(For users who had completed the [networking setup](##networking-setup-for-first-time-user))** Flash firmware onto STM32 NUCLEO-H743ZI2 using OpenOCD. ```shell openocd -f openocd/openocd.cfg -f openocd/main.cfg ``` **(For users who had completed the [networking setup](##networking-setup-for-first-time-user))** Alternatively, an equivalent Nix command can also do the work. ```shell openocd-flash ``` ## Networking Setup for First-time User Provide them to setup Humpback-DDS: - IP Address of Humpback-DDS - Bit length of the LAN prefix - MAC Address of Humpback-DDS - IP Address of MQTT broker - Device name of Humpback-DDS Note: - IP/MAC address of Humpback-DDS should be unique inside a local area network. - Device name should be unique among all Humpback-DDS connected to the same MQTT broker. - The MQTT broker must accept TCP connection at port `1883`. Use the following Nix command. ```shell openocd-flash-customised "" ``` Parameters: - client_cidr_ip_addr: IP address for the Humpback-DDS device, in CIDR notation - mac_addr: MAC address for the Humpback-DDS device - broker_addr: IP address for the MQTT broker - name: Device name of the Humpback-DDS ### Example ```shell openocd-flash-customised 192.168.1.200/24 AC:6F:7A:DE:D6:C8 192.168.1.125 "Urukul" ``` The device will be named `Urukul`. It has `192.168.1.200` as IPv4 Address, inside a `/24` network, with `AC:6F:7A:DE:D6:C8` as MAC address. It will connect to a MQTT broker at `192.168.1.125:1883`. ## MQTT Broker Mosquitto is within the Nix package. Starting a Mosquitto MQTT broker can be as simple as the following line. ```shell mosquitto ``` The MQTT broker will be started locally, at port 1883. To enable feedback from the device, subscribe to all subtopics under `Urukul/Feedback` on a separate terminal. ```shell mosquitto_sub -h -t Urukul/Feedback/# ``` Note that subscription to the feedback topic is completely optional. ## Publishing MQTT messages through Mosquitto Controlling Humpback-DDS can be achieved by sending specific MQTT commands through Mosquitto. The device will listen to all publishes that are under the `Urukul/Control` or `/Urukul/Control` topic. A MQTT message can be published by the following command. ```shell mosquitto_pub -h -t -m ``` For example, to publish a local MQTT broker, with the topic of `Foo/Bar` and `Baz` as the message, enter this command. ```shell mosquitto_pub -h localhost -t Foo/Bar -m "Baz" ``` Note that MQTT topics are case-sensitive. Alternatively, the following nix command provided by the shell simplify the syntax. ```shell publish-mqtt ``` This will send the MQTT message to a local MQTT broker at port 1883, with specified topic and message. The example above can be simplified into: ```shell publish-mqtt Foo/Bar "baz" ``` ## List of Commands All currently supported commands are listed below. **Note: The following table only lists the subtopic. To make a full topic, add `Urukul/Control/` in front of all subtopics.** ### Example: Full topic of Reset command ```shell Urukul/Control/Reset ``` | Subtopic | Message | Functionality| | --------------------------------------- | ------------------------------------------------------------------ | ---| | `Reset` | | Reset the device| | `ChannelX/Switch` | `` | Turn off/on the RF switch at channel X.| | `ChannelX/Attenuation` | ` [dB]` | Set attenuation of channel X.| | `Clock/Source` | `` | Select the clock source of Urukul.| | `Clock/Frequency` | ` [unit]` | Set the clock frequency of the clock source of Urukul.| | `Clock/Source` | `` | Set the clock division of Urukul.| | `Clock` | `frequency: [unit], source: , division: `| Setup the clock tree for Urukul.| | `ChannelX/SystemClock` | ` [unit]` | Set the system clock frequency of channel X.| | `ChannelX/ProfileY/Singletone/Frequency`| ` [unit]` | Setup a single tone profile at channel X, profile Y, with frequency ` [unit]`.| | `ChannelX/ProfileY/Singletone/Amplitude`| `` | Setup a single tone profile at channel X, profile Y, with amplitude factor ``.| | `ChannelX/ProfileY/Singletone/Phase` | ` [deg]` | Setup a single tone profile at channel X, profile Y, with phase ` [deg]`.| | `ChannelX/ProfileY/Singletone` | `frequency: [unit], amplitude: , phase: ` | Setup a compelte single tone profile at channel X, profile Y.| | `Profile` | `` | Switch to a DDS profile across all channels.| ## Reset the device - Topic: `Urukul/Control/Reset` - Message: (Don't care) The `Reset` command resets the device. The effects are: - Turn off all RF switches. - Set attenuations to be 31.5 dB for all attenuators. - Set Urukul clock source to be the internal oscillator, with 100MHz. - Set Urukul clock divider to 4. - Reset all 4 DDS chips. ### Example ```shell publish-mqtt Urukul/Control/Reset ``` This resets the device. ## RF Switch - Topic: `Urukul/Control/Channel/Switch` - ch_num: The channel number, from 0 to 3. - Message: `` This command turns off/on an RF switch of a channel. ### Example ```shell publish-mqtt Urukul/Control/Channel0/Switch "on" ``` This turns on the channel 0 RF switch. ## Attenuator - Topic: `Urukul/Control/Channel/Attenuator` - ch_num: The channel number, from 0 to 3. - Message: ` [dB]` - atten: Attenuation of the attenuator of the specified channel. The unit dB is optional. Valid attenuation is within [0, 31.5] (inclusive) in decibel. ### Example ```shell publish-mqtt Urukul/Control/Channel0/Attenuator "20 dB" ``` This sets the attenuation of the channel 0 attenuator to be 20 dB. ## Urukul Clock Tree 1. Clock Frequency - Topic: `Urukul/Control/Clock/Frequency` - Message: ` [unit]` - f_clk: Clock frequency of the Urukul clock source. - unit: **(Optional)** Unit of f_clk, supports `Hz`, `kHz`, `MHz`, `GHz`. `Hz` if unspecified. ### Example ```shell publish-mqtt Urukul/Control/Clock/Frequency "100 MHz" ``` This sets the clock frequency of Urukul to be 100 MHz. 2. Clock Source - Topic: `Urukul/Control/Clock/Source` - Message: `` - clk_src: Clock source of Urukul. It can only be `OSC`, `MMCX` and `SMA`. ### Example ```shell publish-mqtt Urukul/Control/Clock/Source "OSC" ``` This sets the clock source of Urukul to be the internal oscillator. (Note: The internal oscillator should have a frequency of 100MHz, though this command does not setup the clock frequency.) 3. Clock Frequency Division - Topic: `Urukul/Control/Clock/Division` - Message: `` - clk_div: Clock frequency division of Urukul. It can only be 1, 2, or 4. ### Example ```shell publish-mqtt Urukul/Control/Clock/Division "4" ``` This divides the clock frequency of Urukul by a factor of 4. 4. Clock Overall Setup - Topic: `Urukul/Control/Clock` - Message: `frequency: [unit], source: , division: ` - f_clk, unit, clk_src, clk_div: Same as above. - Argument can be permutated. ### Example ```shell publish-mqtt Urukul/Control/Clock "source: OSC, frequency: 100 MHz, division: 4" ``` This is identical to the 3 examples above. ## DDS System Clock Frequency - Topic: `Channel/SystemClock` - Message: ` [unit]` - f_sys_clk: DDS System Clock frequency a channel. - unit: **(Optional)** Unit of f_clk, supports `Hz`, `kHz`, `MHz`, `GHz`. `Hz` if unspecified. ### Example ```shell publish-mqtt Urukul/Control/Chammel1/SystemClock "1 GHz" ``` This sets the system clock frequency of channel 1 to 1 GHz. ## Single Tone Profile 1. Single Tone Frequency - Topic: `Urukul/Control/Channel/Profile/Singletone/Frequency` - ch_num: Channel number. - pr_num: Profile number. - Message: ` [unit]` - freq: Output frequency of the DDS single tone profile. - unit: **(Optional)** Unit of freq, supports `Hz`, `kHz`, `MHz`, `GHz`. `Hz` if unspecified. ### Example ```shell publish-mqtt Urukul/Control/Channel1/Profile2/Singletone/Frequency "3 MHz" ``` This sets the output frequency of the single tone profile at channel 1, profile 2 to be 3 MHz. 2. Single Tone Amplitude - Topic: `Urukul/Control/Channel/Profile/Singletone/Amplitude` - ch_num: Channel number. - pr_num: Profile number. - Message: `` - ampl: Amplitude factor of the single tone profile. It ranges from 0 to 1 inclusive. ### Example ```shell publish-mqtt Urukul/Control/Channel1/Profile2/Singletone/Amplitude "0.5" ``` This sets the output amplitude factor of the single tone profile at channel 1, profile 2 to be 0.5. 3. Single Tone Phase - Topic: `Urukul/Control/Channel/Profile/Singletone/Phase` - ch_num: Channel number. - pr_num: Profile number. - Message: `` - phase: Phase of the single tone profile. The unit is in degree. - deg: **Optional** Specifies the unit of phase to be degree. ### Example ```shell publish-mqtt Urukul/Control/Channel1/Profile2/Singletone/Degree "0.0 deg" ``` This sets the phase of the single tone profile at channel 1, profile 2 to be 0 degree. 4. Single Tone Overall Setup - Topic: `Urukul/Control/Channel/Profile/Singletone` - Message: `frequency: [unit], amplitude: , phase: ` - All parameters are the same as above commands. - Argument can be permutated. ### Example ```shell publish-mqtt Urukul/Control/Clock "frequency: 3 MHz, phase: 0.0 deg, amplitude: 0.5" ``` This is identical to the 3 examples above. ## Switching DDS Profile - Topic: `Urukul/Control/Profile` - Message: `` ### Example ```shell publish-mqtt Urukul/Control/Profile "5" ``` This is selects profile 5 for all DDS channels.