Pulse Width Modulation (PWM) is a common method used to apply a proportional control signal to an external device using a digital output pin. For example, servo motors use the pulse width of an incoming PWM signal to determine their rotation angle. LCD displays adjust their brightness based on a PWM signal's average value.
PWM is a digital (i.e. square wave) signal that oscillates according to a given frequency and duty cycle.
- The frequency (expressed in Hz) describes how often the output pulse repeats.
- The period is the time each cycle takes and is the inverse of frequency.
- The duty cycle (expressed as a percentage) describes the width of the pulse within that frequency window.
For example, a PWM signal set to 50% duty is active for half of each cycle:
You can adjust the duty cycle to increase or decrease the average "on" time of the signal. The following diagram shows pulse trains at 0%, 25%, and 100% duty:
Adding the required permission
Add the required permission for this API to your app's manifest file:
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
Managing the connection
In order to open a connection to a PWM port, you need to know the unique port name. During the initial stages of development, or when porting an app to new hardware, it's helpful to discover all the available port names from PeripheralManager using getPwmList():
Kotlin
val manager = PeripheralManager.getInstance() val portList: List<String> = manager.pwmList if (portList.isEmpty()) { Log.i(TAG, "No PWM port available on this device.") } else { Log.i(TAG, "List of available ports: $portList") }
Java
PeripheralManager manager = PeripheralManager.getInstance(); List<String> portList = manager.getPwmList(); if (portList.isEmpty()) { Log.i(TAG, "No PWM port available on this device."); } else { Log.i(TAG, "List of available ports: " + portList); }
Once you know the target name, use PeripheralManager to connect to that port. When you are done communicating with the PWM port, close the connection to free up resources. Additionally, you cannot open a new connection to the port until the existing connection is closed. To close the connection, use the port's close() method.
Kotlin
// PWM Name private const val PWM_NAME = ... class HomeActivity : Activity() { private var pwm: Pwm? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Attempt to access the PWM port pwm = try { PeripheralManager.getInstance() .openPwm(PWM_NAME) } catch (e: IOException) { Log.w(TAG, "Unable to access PWM", e) null } } override fun onDestroy() { super.onDestroy() try { pwm?.close() pwm = null } catch (e: IOException) { Log.w(TAG, "Unable to close PWM", e) } } }
Java
public class HomeActivity extends Activity { // PWM Name private static final String PWM_NAME = ...; private Pwm pwm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Attempt to access the PWM port try { PeripheralManager manager = PeripheralManager.getInstance(); pwm = manager.openPwm(PWM_NAME); } catch (IOException e) { Log.w(TAG, "Unable to access PWM", e); } } @Override protected void onDestroy() { super.onDestroy(); if (pwm != null) { try { pwm.close(); pwm = null; } catch (IOException e) { Log.w(TAG, "Unable to close PWM", e); } } } }
Configuring and controlling the PWM signal
After making a connection, configure the timing parameters for the PWM signal. You must set these parameters before activating the signal the first time. To activate the PWM signal, call setEnabled(true). If you need to temporarily de-activate the signal, you can call setEnabled(false).
The following example configures the PWM to cycle at 120Hz (period of 8.33ms) with a duty of 25% (on-time of 2.08ms every cycle):
Kotlin
@Throws(IOException::class) fun initializePwm(pwm: Pwm) { pwm.apply { setPwmFrequencyHz(120.0) setPwmDutyCycle(25.0) // Enable the PWM signal setEnabled(true) } }
Java
public void initializePwm(Pwm pwm) throws IOException { pwm.setPwmFrequencyHz(120); pwm.setPwmDutyCycle(25); // Enable the PWM signal pwm.setEnabled(true); }