My previous post was about how to receive serial signal data from a (pre-bound) SPM9645 remote receiver with RPi's GPIO. Now I will show you how to control an ESC based on the received remote control data using an Adafruit 16-Channel 12-bit PWM driver. Maybe this information can be useful to you or it can save you some time. I am happy for some feedback or optimization proposals etc.
The PWM driver should be connected to the following RPi GPIO Pins (*):
- [P1-01] 3.3V -> VCC
- [P1-03] I2C0_SDA -> SDA
- [P1-05] I2C0_SCL -> SCL
- [P1-09] GROUND -> GND
The SPM9645 receiver should be connected to the following RPi GPIO Pins (*):
- [P1-17] 3.3V
- [P1-06] GROUND
- [P1-10] GPIO 15 (RXD)
Before you are able to control the PWM driver you have to make sure that I2C-support is enabled within the RPi Linux system settings. Remove the following line from your /etc/modprobe.d/raspi-blacklist.conf:
blacklist i2c-bcm2708
Add the following line to your /etc/modules:
i2c-dev
Reboot the device to apply the changes.
The python module smbus is used to control the PWM driver via the I2C interface. All I2C control functionality needed for the driver is based on two smbus functions: write_byte_data() and read_byte_data(). After you have set the PWM frequency the PWM value of each PWM-channel can simply be set with two write_byte_data() function calls.
For the I2C communication the following functions are used:
def I2C_read(reg):
try:
return I2CBus.read_byte_data(I2CAddress, reg)
except IOError, err:
print err
def I2C_write(reg, value):
try:
I2CBus.write_byte_data(I2CAddress, reg, value)
except IOError, err:
print err
Before you can send PWM pulses to the ESC you initially need to setup the PWM frequency used. The frequency defines the total length of a PWM frame and also the length of a PWM tick. The PWM_freq() function might look a bit complicated due to the fact that the current mode of the PWM chip is stored, the chip is put into sleep state and then back up again.
def PWM_freq(freq):
MODE1 = 0x00
PRESCALE = 0xFE
prescaleval = 25000000.0
prescaleval /= 4096.0
prescaleval /= float(freq)
prescale = floor(prescaleval - 0.5)
oldmode = I2C_read(MODE1);
I2C_write(MODE1, (oldmode & 0x7F) | 0x10)
I2C_write(PRESCALE, int(prescale))
I2C_write(MODE1, oldmode)
sleep(0.005)
I2C_write(MODE1, oldmode | 0x80)
Now you should be able to control the RPM of motor connected with your ESC by using the PWM_set() function. The value range depends on the chosen PWM frequency and the minimum/maximum supported PWM pulse lengths of your ESC (firmware). A higher PWM frequency can get a higher control resolution (number of steps).
Sample calculations of PWM_set() ranges:
Minimum PWM pulse length (ESC firmware): 1400 microseconds
Maximum PWM pulse length (ESC firmware): 2000 microseconds
Min @ 60Hz: 1400 / (1000000.0/4096/60) = 344.06
Max @ 60Hz: 2000 / (1000000.0/4096/60) = 491.52
Min @ 100Hz: 1400 / (1000000.0/4096/100) = 573.44
Max @ 100Hz: 2000 / (1000000.0/4096/100) = 819.2
Min @ 400Hz: 1400 / (1000000.0/4096/400) = 2293.76
Max @ 400Hz: 2000 / (1000000.0/4096/400) = 3276.8
Finally you must choose the DSMX channel to use and find out its value range. You can use the following script for this purpose. Download the script (dx6i_info.py)
DSMX Channel Summary:
THR : 306 - 1738 (1432)
AIL : 316 - 1724 (1408)
ELE : 326 - 1734 (1408)
RUD : 318 - 1738 (1420)
Download the POC code here (dx6i_rc.py)
(Default PWM Frequency: 400Hz, Channel: THR ~ 300-1750, ESC connected PWM-Channel: 0)
(Test Hardware: TURNIGY PLUSH 6A (original firmware) - Hacker A10-12S - Power Supply: 7.5V/20A)
! Test WITHOUT PROPELLER - please !
Have fun and stay tuned.
References:
Notes:
- A Spektrum DX6i transmitter was used for the communication with the receiver
- All Spekrum DX6i channel trims were set to 100%
- The Adafruit PWM driver does not a (separate) power supply connected
- It is not possible to use the suggested baudrate of 125000
(* of course you can use any other 3.3V or GROUND Pin)