From 3c804d76c065dcd4d73c194c4b4ff1e120a6df41 Mon Sep 17 00:00:00 2001 From: Jared Rhodes Date: Tue, 10 Jun 2025 08:12:17 -0400 Subject: [PATCH] Add audio jack demo documentation and script --- README.md | 7 ++ docs/audio_jack_demo.md | 166 ++++++++++++++++++++++++++++++++++++++++ src/pi/README.md | 16 ++++ src/pi/audio_demo.py | 29 +++++++ 4 files changed, 218 insertions(+) create mode 100644 docs/audio_jack_demo.md create mode 100644 src/pi/audio_demo.py diff --git a/README.md b/README.md index 9e0efa7..6f39a28 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,10 @@ exact steps). Two demos are provided: The helper script `src/pi/run_serial_demo.sh` loads `g_serial` if needed and launches the Python demo. + +## Audio Jack Telemetry + +An additional demo shows how to transmit sensor data through the Pi’s audio jack. See +`docs/audio_jack_demo.md` and the script `src/pi/audio_demo.py` for details on +connecting a TRRS cable and using the `minimodem` tool to send readings as audio +tones. diff --git a/docs/audio_jack_demo.md b/docs/audio_jack_demo.md new file mode 100644 index 0000000..ec2593d --- /dev/null +++ b/docs/audio_jack_demo.md @@ -0,0 +1,166 @@ +# Data Transmission and Sensor Integration via the Audio Jack + +## Overview + +Using a 3.5mm audio jack as a data interface is an unconventional but intriguing approach to connect sensors or transmit data between devices. Instead of only playing sound, the audio jack can carry encoded information via audio-frequency signals. This technique has been used in various gadgets – from credit card readers to sensor dongles – that plug into a phone’s headphone jack. In our context, we explore how to leverage the audio jack on both **Raspberry Pi** (for models that have an audio output jack) and **mobile devices** (via a .NET MAUI app) to transmit sensor readings or other data. We’ll discuss how such an “audio link” works, what hardware modifications are needed, how to implement it in .NET MAUI, and propose a demo scenario for the `QiMata/mobileiot` project. + +## Audio Jack as a Data Interface + +The 3.5mm headphone jack can act as a rudimentary data port by using sound waves to encode digital information. Essentially, this is like using a pair of **mini-modems** – one device generates audio tones representing data, and the other listens and decodes those tones back into data. Several real-world products have used this method: + +* **Square credit card readers** and other magstripe readers send card data through the microphone input as audio signals. +* **Health and sensor peripherals** (e.g. iHealth’s glucometer, Irdroid IR blaster) transmit readings or commands via audio jack. These often modulate sensor data into audio-frequency signals. +* **NXP Quick-Jack and Project HiJack:** sensor interface boards that harvest power from the audio jack and use it for sensor modules, communicating via audio signals. *(More on Quick-Jack in a later section.)* + +**How it works:** The device (e.g. a Raspberry Pi or a microcontroller) converts digital data into an analog audio waveform. The mobile device’s headphone **microphone input** then picks up this waveform. By analyzing the recorded audio, the .NET MAUI app can recover the original data (after demodulation and decoding). This process is analogous to old telephone modems or fax machines which used audio frequencies to transmit bits. Crucially, the audio signal must be **AC-coupled** (no DC component) because headphone jacks transmit AC audio; any data must be encoded as audible or near-ultrasonic tones or pulses. The available bandwidth (roughly 20 Hz to 20 kHz for typical audio) limits the data rate, but even a few hundred bits per second is achievable with robust encoding. + +## Raspberry Pi Audio Jack Capabilities & Wiring + +Most Raspberry Pi models (A/B/2/3/4) include a 3.5mm analog output jack for audio. This jack is typically a **TRRS connector** carrying stereo audio (Left/Right channels) and sometimes composite video, but **no microphone input line**. In other words, the Pi’s built-in jack **supports audio output only, not input**. This is important when planning sensor integration: you cannot directly record analog signals from a sensor into the Pi’s jack without extra hardware. Options to get audio *into* a Pi include USB sound cards or I2S audio ADC boards (e.g. the Wolfson/DAC HATs). + +For our purposes, a plausible setup is to use the Raspberry Pi as the **transmitter** (sending data out via its audio jack), and a mobile device as the **receiver** (using its microphone input). This plays to the Pi’s strength (audio out) and the phone’s ability to record audio. The physical connection would be through a 3.5mm audio cable: one end plugs into the Pi’s audio jack, the other into the phone’s headphone+mic jack (TRRS). + +**Figure: TRRS audio plug standards (OMTP vs CTIA)** – The microphone (Mic) and ground contacts are swapped between the two standards. Modern smartphones (CTIA standard) use Tip=Left, Ring1=Right, Ring2=Ground, **Sleeve=Mic**. When wiring the Raspberry Pi to a phone, ensure the Pi’s audio **ground** connects to the phone’s ground, and one of the Pi’s audio **output channels** (Left or Right) feeds into the phone’s **mic sleeve** contact. (A TRRS breakout or CTIA adapter can help ensure the correct mapping.) + +**Circuit considerations:** We must condition the Pi’s audio output for the phone’s mic input: + +* **Level and Impedance:** The Pi’s line-out audio (~1 Vpp) should be attenuated to a safe microphone level (tens of millivolts). A simple resistor divider can achieve this attenuation. Additionally, the phone expects a certain impedance on the mic line (to detect that an external mic is connected). Typically a few hundred ohms to ground via the mic line is used to signal mic presence. In practice, one might place a resistor (e.g. ~1 kΩ) from the mic line to ground. +* **DC bias and coupling:** Smartphone mic inputs provide a bias voltage (~2–3 V through a resistor) to power electret microphones. We must use a **capacitor (capacitive coupling)** on the signal line to block this DC bias from feeding back into the Pi. For example, placing a ~10 µF capacitor in series between the Pi’s audio output and the mic input allows AC audio through but stops DC. The bias will then appear across the resistor to ground, which mimics the microphone’s load. +* **Stereo vs Mono:** The Pi outputs stereo, but the phone’s mic input is mono. We can use just one channel (e.g. Left channel) for data. It could be wise to output the same data on both L and R (for instance, R channel could be a constant tone for power – an idea from HiJack/Quick-Jack – but in our simple case we may leave R unconnected or duplicate data on it for redundancy). + +If instead we wanted the **phone to transmit to the Pi**, note that the Pi lacks an analog input jack. You’d need to attach a USB sound card or similar to the Pi to receive the audio. That is feasible (the Pi could then decode audio data coming from the phone), but for simplicity, our demo will focus on Pi-to-phone data transmission. (The Pi can always send data it gathers from sensors over this “audio link” to the mobile app.) + +## Data Encoding and Modulation Techniques + +To send bits over audio, we need an **encoding scheme** that turns digital 0/1 values into sound waves. Several approaches are possible, each with trade-offs in complexity and reliability: + +* **Frequency Shift Keying (FSK):** This is a common method where a “0” bit is represented by a tone of one frequency, and a “1” by a tone of another frequency. For example, classic Bell 202 modems used about 1200 Hz vs 2200 Hz for 0/1. The transmitter rapidly switches between these two tones to form a serial bit stream. **Frequency-shift keying** works well through AC-coupled audio because a continuous tone is always present (just its frequency changes). It’s also fairly robust to noise. We’d choose two frequencies comfortably within the audio passband (e.g. 1 kHz and 2 kHz) and ensure the phone’s mic can pick them up. Data rate can be modest (hundreds of bits per second) depending on how fast we toggle the tones. + + **Figure: FSK modulation example** – An example of binary data (top, in blue) being modulated into an FSK audio waveform (bottom) with two distinct frequencies representing bits. In practice, the waveform would be sent through the audio jack, and the receiver would detect the frequency shifts to recover the bitstream. + +* **Dual-Tone Multi-Frequency (DTMF):** This is the telephone signaling method (think touch-tone dial pads) which uses a combination of two tones for each symbol (0-9,*#, A-D). DTMF could be repurposed to transmit data in chunks (each DTMF “digit” encodes 4 bits if using 16 symbols). For example, one could encode numeric sensor readings directly as DTMF digits. The advantage is that DTMF decoding algorithms are well-known – we could even leverage existing libraries. (There is a NuGet package `DtmfDetection.NAudio` for detecting DTMF tones in audio streams, which could guide our implementation.) The downside is relatively slow symbol rate and the need to output two tones simultaneously for each digit, but given our low data volume (sensor readings), this is acceptable. + +* **On-Off Keying / ASK:** Simplest conceptually – turn a tone on for “1”, off for “0” (or vary amplitude). However, pure on-off keying over audio is problematic because long “off” periods are silence (near 0 Hz DC component), which gets blocked by capacitors (the receiver might not distinguish a sustained “0” vs “no signal”). A variant is to use a **pulse** for “1” and no pulse for “0” in fixed time slots (similar to Morse code dots/dashes but machine-speed). This needs careful timing synchronization. Generally, FSK or Manchester coding is preferred to ensure there’s always some alternating signal. + +* **Manchester Coding:** This is a bit encoding (used in Ethernet, RFID, etc.) where each bit period contains a transition in the middle – e.g. 0 = tone high then low, 1 = tone low then high. Manchester coding ensures a continuous clock component and no DC bias. NXP’s Quick-Jack solution actually uses Manchester-coded data over the audio jack. We could combine this with a single frequency (transition vs no transition) or two frequencies. Manchester encoding might simplify clock recovery at the receiver since each bit is a known duration. In our case, we might not need full Manchester if we send short bursts, but it’s an option for reliability. + +**Protocol & framing:** Regardless of modulation, it’s wise to frame the data with a preamble or known sequence so the receiver can detect start-of-message and possibly calibrate. For example, start each data burst with a specific tone or DTMF digit sequence (“header”) before the actual sensor data, to allow the decoder to sync up. If using raw FSK bits, a preamble of alternating 1/0 (a tone oscillation) can help the receiver lock onto the frequency and phase. For a simple demo, we might send very short packets (just a few bytes or one sensor reading at a time) to ease decoding. + +## .NET MAUI: Audio Capture and Signal Processing + +On the mobile side, a .NET MAUI application can access the device’s audio input and output capabilities. Although .NET MAUI does not have built-in audio APIs in the core, we can use libraries and platform-specific features: + +* **Audio Recording/Playback:** The open-source **Plugin.Maui.Audio** provides cross-platform audio support, including recording from the microphone and playing audio, for .NET MAUI apps. By adding this plugin, our app can start the microphone, capture PCM audio data, and also generate or play audio signals. +* **Platform APIs:** Alternatively, on Android one could use `AudioRecord`/`AudioTrack` via Xamarin/MAUI dependency services, and on iOS use `AVAudioRecorder`/`AVAudioPlayer`. However, the Plugin simplifies this by offering a single API. Under the hood it uses the native audio APIs for each platform. + +Once we can record audio, the next step is **signal processing** to decode the data: + +* **Reading PCM data:** The microphone input will give us a stream of PCM samples (e.g. 16-bit PCM at 44.1 kHz or 8 kHz). We may not need full 44.1 kHz for our data; using a lower sample rate like 8 kHz or 11.025 kHz is sufficient for voice-band signals and reduces processing. (On Android, you can often specify the recording sample rate. The CodeProject example used 8 kHz for FSK.) +* **Demodulation:** If we use FSK, we need to detect which frequency is present over time. A common approach is to run a **Goertzel algorithm** or FFT on chunks of audio to measure energy at the expected frequencies. For example, to decode FSK at 1 kHz vs 2 kHz, we can compute the power at 1 kHz and 2 kHz for each time slice; whichever is stronger tells us the bit. This can be done in real-time as we read the samples. If using DTMF (which has 8 possible frequencies), we could apply a DTMF detection library or implement Goertzel for each tone frequency. +* **Decoding bits/bytes:** After determining the bit stream from the audio, we reassemble bytes or numbers. For instance, if we send ASCII characters or a binary value, the app would buffer bits until it has a whole payload, then convert to a useful value (like a temperature reading). In the case of DTMF, decoding gives us digits directly, so if we encoded “25.3” as digits, we get the string back. +* **Error handling:** Audio transmission can be noisy, so consider adding simple error checking like a checksum byte or repeating the message a few times. In a demo, this might be unnecessary if the connection is via cable (which is relatively noise-free), but it’s good to note. + +On the transmit side (e.g. if the app were to send audio out), we can generate tones in software. For example, to generate a sine wave of a given frequency, one can fill a buffer with samples according to `sin(2π f t)`. In C# this is straightforward using `Math.Sin`. For instance, generating a 1 kHz tone at 8000 Hz sample rate for 0.1 sec: + +```csharp +int fs = 8000; +int durationMs = 100; +int sampleCount = fs * durationMs / 1000; +short[] samples = new short[sampleCount]; +double freq = 1000.0; +for(int n = 0; n < sampleCount; n++) { + double t = (double)n / fs; + // sine wave from -1 to 1, scaled to 16-bit amplitude + short sample = (short)(Math.Sin(2 * Math.PI * freq * t) * 32767); + samples[n] = sample; +} +// (Then, play these samples via AudioPlayer or output stream) +``` + +Using this method, we can create audio buffers for any pattern of tones (FSK or DTMF sequences). The Plugin.Maui.Audio `AudioPlayer` can likely play raw PCM if we wrap it in a Stream with a WAV header, or we might use platform-specific playback of PCM. (Alternatively, one could generate a WAV file and use the plugin to play it.) + +## Practical Use Cases and Demo Ideas + +Using audio jack communication is admittedly a niche (given the prevalence of Bluetooth, USB, WiFi), but it’s a fun fallback and educational demo. Here are a few realistic scenarios that could be demonstrated: + +* **Pi-to-Mobile Data Telemetry:** The Raspberry Pi collects a sensor reading (e.g. temperature from a DHT22 or CPU temperature) and sends it as an encoded audio signal to the phone. The phone app decodes it and displays the value live. This could be done at intervals (say the Pi sends a reading every 5 seconds via a short audio burst). It showcases one-way data transfer using just an audio cable. For instance, the Pi could encode the temperature as a two-digit number via DTMF or as a binary value via FSK. The .NET MAUI app would listen and update the UI with the latest value. + *Feasibility:* Very high – only requires the cable hookup and software on both ends. Data volume is small. This is a recommended demo due to simplicity. + +* **Bidirectional Audio “Chat”:** With additional hardware (USB audio input for Pi or an output from phone), one could establish two-way communication – essentially using audio as a serial link. For example, a phone could send a command tone to the Pi to trigger an action, and the Pi responds with data tones. However, full duplex would be challenging without splitting channels (e.g. Pi could transmit on left channel, and listen on right channel via a USB audio dongle, while the phone does the opposite on its end). This might be too complex for a simple demo, but it’s conceptually possible. + +* **Audio-Coupled Sensor Peripheral:** Construct a small sensor module that plugs into the phone’s headphone jack directly. For instance, a **temperature sensor dongle** that uses the mic input: one could use a microcontroller or simple analog circuit that reads a temperature sensor and outputs an audio frequency corresponding to the temperature. The phone app records that and interprets the frequency to get the reading. As a simpler analog example, a thermistor could be part of an oscillator circuit (like a 555 timer) such that the oscillation frequency varies with temperature; the phone then just measures that frequency. This basically turns the phone+dongle into a thermometer. In fact, NXP’s **Quick-Jack** demo board included a temperature sensor and showed the reading on the phone via the audio jack. This use case is very engaging, but it needs custom hardware. For a quick integration into the repository, a fully hardware-based sensor module might be outside scope, unless a prebuilt board is used. (If one had NXP’s Quick-Jack or the older HiJack board, those could be used directly – they implement the audio communication and provide sensor interfaces.) + + **Figure: Quick-Jack demo – phone reading a temperature sensor via the audio jack.** *This image (from NXP’s Quick-Jack demo) shows a smartphone app displaying temperature sent from a plug-in sensor board over the audio jack. Our demo could emulate a simpler version of this concept using Raspberry Pi as the sensor host and a .NET MAUI app as the display.* + +* **Data-Encoded Audio Beacon:** Without a direct cable, one could send data acoustically (through the air) using a speaker and microphone. For example, the Pi could play an ultrasonic or audible tone pattern through a speaker and the phone picks it up via its mic. Projects like **QuietNet** have used near-ultrasonic sound to send messages wirelessly between devices. While intriguing (and it truly makes the Pi a sort of “audio beacon”), it introduces a lot of environmental noise issues and is less reliable. If time permits, it could be a secondary demo – e.g., a simple one-way message broadcast using a small speaker. However, for clarity and consistency, the wired approach (audio cable) is easier to demonstrate. + +In summary, the most viable demo for QiMata’s MobileIoT app is **using the Pi’s audio-out to send a sensor value to the .NET MAUI app using an audio cable connection.** This complements the existing demos (BLE, USB) by showcasing yet another communications channel. It’s a one-way stream (which is fine for many sensor scenarios). + +## Implementation Plan for the Demo in QiMata/mobileiot + +To integrate this into the `QiMata/mobileiot` repository, we can add a new module (both on the Pi side and in the .NET MAUI app) following a pattern similar to the BLE and USB demos. + +**1. Hardware Setup:** Connect the Raspberry Pi’s audio output to the mobile device’s headphone/mic jack. Use a suitable TRRS cable/adapter: + +* If using a smartphone with a headphone jack (CTIA standard), a normal 3.5mm male-to-male cable plus a CTIA splitter (to break out mic vs headphones) can be used. Wire Pi’s ground to mic ground, Pi’s left-out to mic input through a series capacitor (10µF) and an attenuating resistor (e.g. 1kΩ to ground from mic line as described above). Alternatively, hack a cheap smartphone headset: cut the microphone and splice the Pi output there. Ensure volume on Pi is set low to avoid saturating the mic. +* If the phone has **no headphone jack** (common in newer phones), you can use a USB-C to 3.5mm adapter that supports mic input (most do). Or use a different receiver like a laptop running the MAUI app (Windows MAUI app could listen via PC microphone jack). +* Optionally, connect an actual sensor to the Pi (like a DHT22 temperature/humidity sensor on GPIO) so that the data being sent is real sensor data. If no sensor, Pi can send a dummy incrementing number or a value read from Pi’s on-board sensors (e.g. CPU temperature from `/sys`). + +**2. Raspberry Pi Side (Transmitter):** Implement a Python script or .NET code to continuously transmit data via audio: + +* In `src/pi`, add a script e.g. `audio_demo.py`. This script would read the sensor (or generate data), encode it, and play it through the audio output. Python can use libraries like `numpy` to generate waveforms and `pyaudio` or system calls to play sound. A simpler method: use the Linux `minimodem` tool to handle modulation. For instance, Pi can execute: + + ```bash + echo "23.5" | minimodem --tx --quiet 1200 + ``` + + which would output the text "23.5" as an audio FSK signal at 1200 baud to the default audio output. (Minimodem supports various baud rates and FSK schemes, and can also **receive**, so it’s a handy tool for testing.) We can incorporate `minimodem` if installing it is acceptable, or simply precompute tones in Python. +* If doing it in pure Python: for example, to send a byte, generate a waveform for each bit: frequency f1 for 0, f2 for 1, append them. Or generate DTMF tones for each digit of a reading (there are Python libraries for DTMF or one can mix two sine waves). +* Set the Raspberry Pi’s audio output to a fixed volume. Use ALSA or `alsamixer` to ensure the output level is appropriate (start low to avoid clipping the phone input). +* This script will run continuously or periodically. It can be started similarly to other demos (maybe via a systemd service or on demand). Documentation in `src/pi/README.md` should be updated with instructions (e.g. “ensure minimodem is installed via apt” or “run audio_demo.py with Python”). + +**3. Mobile App Side (Receiver):** In the .NET MAUI app (`src/MobileIoT`): + +* Add a new page or section for the **Audio Jack Demo**. This page will have UI to start/stop listening and to display the received data (for example, a label for the latest sensor value, maybe a scrolling log of messages). +* Use `Plugin.Maui.Audio` to access the microphone. Upon starting, request microphone permissions from the user (both Android and iOS require runtime permission for microphone). Then create an `AudioRecorder` or audio stream. +* As audio data comes in, feed it into a decoder. Depending on chosen encoding: + + * If using DTMF: we can incorporate a simple DTMF detection. Perhaps include the `DtmfDetection` library if it’s compatible, or port the core logic (Goertzel algorithm coefficients for the 8 frequencies). We know the standard DTMF frequencies (697,770,852,941 Hz for low group and 1209,1336,1477,1633 Hz for high group). For a simpler set (0-9), only 697,770,852,941 + 1209,1336,1477 are used. The code can perform an FFT on small chunks (like 50 ms of audio) to identify tone pairs. The Stack Overflow reference shows sample code is available for guidance. Once a DTMF digit is recognized, append it to a buffer until a pause or end-of-message, then parse the number. + * If using a binary FSK approach at a set baud: implement a phase-locked loop or simpler algorithm. For demo simplicity, DTMF or even just single-frequency on/off keyed bits might be easier to implement than a full FSK demodulator in C#. Another option: utilize **Quietnet’s approach** – they use an FFT to detect a frequency around 19 kHz for bits. However, to keep it audible and easier to debug, 1–2 kHz range is fine. + * You could also record a short snippet and run a post-processing (not real-time) to decode. But real-time (or near-real-time) decoding provides a better demo effect. +* Display the decoded information on screen. For example, show “Temperature: 23.5 °C” once the message is decoded. If messages repeat, update continuously. +* Error handling: If decoding fails (e.g. unrecognized tone), the app can simply ignore or show “—” for that cycle. + +**4. Testing and Tuning:** It’s crucial to test with the actual hardware connection. Adjust the Pi’s audio volume and the app’s decode sensitivity. Using a scope or logic analyzer on the audio line can help verify signal shape and amplitude. Also, if using DTMF or multiple frequencies, test that the phone’s mic frequency response is adequate (most phone mics can handle up to ~4 kHz well; beyond that, phone audio input might have low-pass filtering). If using a high-frequency approach (ultrasonic), ensure the phone can still detect it – some phones aggressively filter above 20 kHz. Using audible frequencies (within human hearing) for the demo is fine (the demo sound can be heard as beeps, which is actually a fun visual/auditory cue that data is being transmitted). + +**5. Module Encapsulation:** Structure the code such that all audio handling and decode logic is in a reusable class, e.g., an `AudioModemService` in the MAUI app. This service can handle starting the recording, doing signal processing, and emitting decoded data to the view model. This way, the core logic is separate from UI and could be unit-tested with prerecorded WAV samples. Similarly, the Pi’s script can be written modularly (maybe a function to encode a value to a WAV buffer, etc.). If more polish is desired, one could even create a small **.NET library** that both the Pi (if running .NET) and the MAUI app could share for encoding/decoding (ensuring consistency). However, given the Pi side might be Python, simply agreeing on the protocol is enough. + +Throughout development, leverage open-source references for confidence: + +* Use **Minimodem** on a PC to generate and test-decode sample audio files. For example, generate an audio file of “HELLO” in FSK and see if your C# decoder can decode it. Minimodem can also decode, so you can cross-verify your Pi’s output. +* Look at the Quick-Jack reference design (Manchester-coded) if curious – though implementing Manchester might be overkill, the concept could inspire a simple method (e.g., send a known preamble 0x55 pattern which in Manchester is an alternating tone, easy to detect). + +## Technical Considerations and Limitations + +While implementing data-over-audio is quite feasible, note a few considerations: + +* **Speed and Throughput:** This method is **low-bandwidth**. Even a sophisticated audio modem (like 1200 baud FSK) is about 120 bytes/sec at best. For sensor data, this is usually fine (you rarely need more than a few readings per second), but it’s nowhere near the throughput of BLE or USB. Large data (like sending a file or stream) would be very slow and not practical beyond novelty uses. Our focus is on small telemetry messages. + +* **Reliability:** Audio signals can suffer from noise, interference, or filtering. A direct cable connection minimizes noise, but long cables or poor connections could introduce hum or attenuation. The phone’s input may also apply automatic gain control (AGC), which could complicate amplitude-based schemes (AGC might suppress a constant tone over time). Using frequency-based encoding (FSK/DTMF) mitigates issues with amplitude variation due to AGC, as we only care about presence of frequencies. Still, expect to implement some form of error check (even if just sending the message multiple times or adding a parity bit). + +* **Platform Differences:** Android and iOS may handle audio input differently. Ensure the MAUI plugin is configured correctly (e.g., on Android, use the proper audio source – probably the default mic which automatically takes external mic if plugged in). Also, the **CTIA/OMTP** issue: if, by chance, one device uses the other standard, an adapter may be needed (OMTP <-> CTIA adapter swaps the mic and ground lines). Most modern devices are CTIA. If using an older Android phone from a decade ago, check the standard. The Raspberry Pi’s jack is not a full TRRS with mic, so we rely on the phone’s TRRS. + +* **Power for Sensor Modules:** If designing an audio-coupled sensor (like Quick-Jack/HiJack did), powering the external circuit is a challenge. Quick-Jack uses the audio output’s AC signal and rectifies it to DC. Simpler: use the mic bias line to power a very low-power sensor circuit (the bias can supply a couple of milliamps). But for our Pi demo, the Pi itself has its own power, so this is not an issue – the Pi doesn’t need power from the phone. It’s only an issue if you make a standalone plug-in device. We likely won’t go that far in this demo, but it’s interesting to note that energy harvesting from audio is possible (the Right channel could output a constant 21 kHz tone that a circuit rectifies, while Left channel carries data). + +* **Modern Device Trends:** As noted, many smartphones have ditched the headphone jack. This limits the universal applicability of this method. For demonstration (internal or educational) it’s fine to use what hardware is available (maybe an Android development phone or even a tablet). If targeting a broader audience, one might need an external USB sound card for phones too – which then begs the question, why not use digital methods. But again, our goal here is to enrich the MobileIoT demo suite, and the quirkiness of an “audio modem” demo is itself valuable. + +* **Legal/Safety:** There are no significant safety concerns in transmitting audio between devices at these levels, but if ultrasonic sound were used in air, extremely loud ultrasound could potentially be an irritant or cause speaker issues. We will be in audible or just near-audible range and using a wired link, so none of that is a problem. Just ensure not to drive the Pi audio at max volume into the mic – start low to avoid any possibility of clipping or damage to the phone’s input. + +## Conclusion and Demo Recommendation + +Implementing data transmission via the audio jack is highly doable with some signal processing work. It offers a creative demonstration of hardware/software integration: essentially repurposing the headphone interface as a data port. We’ve examined how to encode digital data as sound (FSK, DTMF, etc.), how to wire a Raspberry Pi to a phone’s mic input (with proper coupling and adapters), and how to use .NET MAUI to capture and decode the signal. + +For the **QiMata/mobileiot** project, the recommended demo is: **Raspberry Pi transmitting a sensor value to the .NET MAUI app using an audio cable connection.** This could be presented as an “Audio Jack Telemetry” feature in the app. The steps would involve minimal hardware (just a cable and a couple of passive components) and open-source libraries like Plugin.Maui.Audio and possibly a signal processing snippet for decoding. It complements the existing BLE and USB demos by showing a fallback communication method requiring no wireless stack or USB host – just analog sound. + +In summary, while not mainstream, audio-jack data transfer is a viable and fun IoT experiment. It teaches about modulation, encoding, and low-level data transfer, fitting well with an educational/demo repository. By encapsulating the encode/decode logic into reusable modules (e.g., an `AudioModem` class for the app, and a Python script or .NET routine for the Pi), this functionality can be cleanly integrated and potentially re-used for other projects. The result will be a unique demo where plugging in a headphone cable suddenly enables an IoT data link – sure to get some amazed reactions when shown in action. + diff --git a/src/pi/README.md b/src/pi/README.md index 8ed6cce..d7d5d83 100644 --- a/src/pi/README.md +++ b/src/pi/README.md @@ -49,4 +49,20 @@ On the host side, the Pi will enumerate as a serial COM port. The MobileIoT app Because the echo functionality is handled in the Pi’s kernel module, **no separate Python script is required for this demo** – the Pi effectively acts as a USB echo device as soon as `g_zero` is enabled. This demo demonstrates how a Raspberry Pi can emulate a custom USB data interface, useful for high-throughput data exchange or testing USB communications. It highlights an IoT integration scenario where a device communicates over USB at the raw data level (bulk endpoints) rather than through a higher-level protocol. +## Audio Jack Telemetry Demo + +**Purpose:** This experimental demo sends sensor data through the Pi’s audio output so a mobile device can receive it via the headphone microphone pin. It illustrates how an audio cable can serve as a low-speed data link when no network or USB connection is available. + +**Hardware:** A Raspberry Pi with a 3.5mm audio jack and a TRRS cable to connect to the phone’s headset jack. A small resistor divider and capacitor should condition the Pi’s line-out level for the phone’s mic input (see `docs/audio_jack_demo.md`). Optionally attach a sensor such as a DHT22 to provide real data. + +**How it Works:** The script `audio_demo.py` reads a value (for example the CPU temperature) and transmits it as FSK tones using the Linux `minimodem` tool. The mobile app records from the mic and decodes the tones back into text. Each reading is sent every few seconds so the app can display the latest value. + +Run the demo with: + +```bash +python3 audio_demo.py +``` + +Ensure `minimodem` is installed (`sudo apt-get install minimodem`) and the Pi audio output volume is set low enough not to clip the phone input. + Overall, these Raspberry Pi demos cover a range of IoT interaction models: **wireless sensor/actuator control via BLE**, **proximity detection via BLE beacon**, and **wired command/data exchange via USB**. Developers can refer to these examples as a guide for integrating Raspberry Pi hardware with mobile or other client applications, adapting the concepts to fit their specific IoT workflows and communication needs. Each demo provides a template for connecting physical hardware (sensors, LEDs, etc.) with software, using industry-standard protocols and Raspberry Pi’s capabilities to bridge the physical and digital worlds in an IoT solution. diff --git a/src/pi/audio_demo.py b/src/pi/audio_demo.py new file mode 100644 index 0000000..3b44177 --- /dev/null +++ b/src/pi/audio_demo.py @@ -0,0 +1,29 @@ +import subprocess +import time + + +def read_cpu_temp(): + """Read the Pi CPU temperature in Celsius.""" + with open('/sys/class/thermal/thermal_zone0/temp') as f: + return float(f.read().strip()) / 1000.0 + + +def transmit(value: float): + """Transmit a numeric value as FSK tones via minimodem.""" + msg = f"{value:.1f}\n" + subprocess.run(['minimodem', '--tx', '--quiet', '1200'], input=msg.encode(), check=False) + + +def main(): + print("Audio telemetry started. Press Ctrl+C to stop.") + try: + while True: + temp = read_cpu_temp() + transmit(temp) + time.sleep(5) + except KeyboardInterrupt: + pass + + +if __name__ == '__main__': + main()