PWM through I2C

Hi, brand new to the forum. Have been playing with Xojo for a few weeks. RPi a few weeks longer than that.

While I’m new to Xojo and RPi, I have done some programming in Visual Basic back in the day, as well as industrial automation, more so PLC’s, and also CoDeSys.

I bought the “I wish I knew how to…” book, and have gone though a lot of it. In the I2C section, I didn’t really see an example that applied to what I was trying to do. Trying to output a PWM signal for LED dimming through a PCA9685 that’s on a Waveshare Fan HAT (https://www.waveshare.com/fan-hat.htm).

Any pointers of where to start on that would be great. I already have my GUI and all variables to go with that already set up. I have a scale of 0-100 for my output value, but obviously that will need to be scaled for output to the PCA9685.

Ha. well as a lapsed electrical engineer, PWM is something I’d let a motor controller (ie hardware) handle directly, those things are cheap as chips (literally) and have outputs to suit whatever the load is (AC/DC motor, stepping motor, heater, fan etc). These can include inputs for a position encoder or tachometer to provide closed-loop feedback, even complete PID control. The RPi would then only need to set a direction bit and speed or position setpoint in whatever format the motor controller used - analog (4…20mA, 0…5V, 0…10V etc) or digital (8 or 16 bits) and let the controller do the rest.

No need to waste CPU time doing something hardware does perfectly well, and in some cases better.

1 Like

I may not have been 100% clear with my description. I guess I’m not sending a PWM signal TO the HAT, but using it to generate the PWM signal. The reason for using this HAT is that it outputs a 5v signal, which works better than 3.3v from the GPIO pins, for the MOSFETs that I’m using to drive the LEDs.

@Rudy_Ryzebol,

After looking for information on the FanHat, it appears that a driver needs to be created in Xojo to command the hat. I am not sure how the commands work with this unit, and some data is available at:
WaveShare Information

One option is to convert an existing driver (possibly written in c?) and convert it to Xojo. I have written a few drivers for other boards, and it can be quite an involved process.

Warm regards.

Thanks for the reply Eugene.

I guess I was under the impression that I2C was essentially just a simple serial communication connection that can send/receive data from different stand alone cards/devices on the connection. Using that device’s address, and then essentially specify a register within that address to read from/write to. That addressed device then executes some function dictated by the information written, or updates that information continuously by it’s function ready to be read again. Is this not how I2C works?

Is interfacing code different for every different device? I thought that was the whole purpose of having one standard communication bus. Can the standard methods contained within pigpio not accomplish this? If not, I guess I’ve been assuming a bit too much.

Thanks,
Rudy

Hello Rudy,

Yes, you are correct that this is the way that it works. The issue is what information is needed to be sent and how to decode the information that has been received?

When working with third-party electronics, I often need to contact the people who made the board to understand how and what communication is needed.

An oversimplified example is:

  1. I2C handshake
  2. send information (what kind of information, Such as display the temperature, increase speed of fan, check voltage, etc?)
  3. Receive information from I2C (Does this confirm fan speed, or provide a binary value of the temperature (in C or F?), and what other information is being sent).

Possibly discuss the communication with the manufacturer to get more information, or possibly rewrite the Python or C code to write a driver in pigpio. Unfortunately, these multi-function devices are typically not easy to recreate the driver.

I usually find that it is much easier to design my own Raspberry Pi Hat with the logic explained in the book, than to rewrite some of the drivers. Its similar to having a button in a program that can turn on the security of a house. Having the button (I2C communication) is the easy part, programming the inter-connected parts, wiring, and code is the challenging part. This may include photos, video, audio, face identification, doors, locks, and much more than a simple button.

Does this possibly help explain it a little more?

That is like saying DNS, telnet, ftp, smtp, shttp, etc should all use the same commands and behave the same because they use a standard communication protocol, tcpip

1 Like

Thanks guys. I kinda get the feeling that this HAT is gonna be a bunch of headache to get it to do what I want. Not that there’s necessarily anything wrong with the HAT itself, because as it is, it’s working. Channel 0 is what runs the fan, and I can steal that signal, which is running about 40%.

This is the example that they provide for Python, if this helps clear up anything. Unfortunately, I don’t know enough yet about doing this to look at this code and pick out the important bits:

#!/usr/bin/python

import time
import math
import smbus

============================================================================

Raspi PCA9685 16-Channel PWM Servo Driver

============================================================================

class PCA9685(object):
# Registers/etc.
__SUBADR1 = 0x02
__SUBADR2 = 0x03
__SUBADR3 = 0x04
__MODE1 = 0x00
__PRESCALE = 0xFE
__LED0_ON_L = 0x06
__LED0_ON_H = 0x07
__LED0_OFF_L = 0x08
__LED0_OFF_H = 0x09
__ALLLED_ON_L = 0xFA
__ALLLED_ON_H = 0xFB
__ALLLED_OFF_L = 0xFC
__ALLLED_OFF_H = 0xFD

def __init__(self, address=0x40, debug=False):
	self.bus = smbus.SMBus(1)
	self.address = address
	self.debug = debug
	if (self.debug):
		print("Reseting PCA9685")
	self.write(self.__MODE1, 0x00)

def write(self, reg, value):
	"Writes an 8-bit value to the specified register/address"
	self.bus.write_byte_data(self.address, reg, value)
	if (self.debug):
		print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))

def read(self, reg):
	"Read an unsigned byte from the I2C device"
	result = self.bus.read_byte_data(self.address, reg)
	if (self.debug):
		print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
	return result

def setPWMFreq(self, freq):
	"Sets the PWM frequency"
	prescaleval = 25000000.0    # 25MHz
	prescaleval /= 4096.0       # 12-bit
	prescaleval /= float(freq)
	prescaleval -= 1.0
	if (self.debug):
		print("Setting PWM frequency to %d Hz" % freq)
		print("Estimated pre-scale: %d" % prescaleval)
	prescale = math.floor(prescaleval + 0.5)
	if (self.debug):
		print("Final pre-scale: %d" % prescale)

	oldmode = self.read(self.__MODE1);
	newmode = (oldmode & 0x7F) | 0x10        # sleep
	self.write(self.__MODE1, newmode)        # go to sleep
	self.write(self.__PRESCALE, int(math.floor(prescale)))
	self.write(self.__MODE1, oldmode)
	time.sleep(0.005)
	self.write(self.__MODE1, oldmode | 0x80)

def setPWM(self, channel, on, off):
	"Sets a single PWM channel"
	self.write(self.__LED0_ON_L+4*channel, on & 0xFF)
	self.write(self.__LED0_ON_H+4*channel, on >> 8)
	self.write(self.__LED0_OFF_L+4*channel, off & 0xFF)
	self.write(self.__LED0_OFF_H+4*channel, off >> 8)
	if (self.debug):
		print("channel: %d  LED_ON: %d LED_OFF: %d" % (channel,on,off))

def setServoPulse(self, channel, pulse):
	pulse = pulse*4095/100       
	self.setPWM(channel, 0, int(pulse))

If anyone has a suggestion of an easier way to achieve my goal, other than using a soft pwm from the gpio pins, I don’t mind buying other little boards.

Allow me to explain what exactly I’m doing with this project. I am building a toy hauler RV trailer, and am using the PI to control essentially everything. Multiple dimming channels for LED lighting, multiple relays controlled to turn on/off various things. This will all be interfaced through a 7" touch screen.

I also have another PI (3B) running the same 7" screen, but running Victron’s Venus OS. This will run everything to do with the solar system, and all of that is up and running already.

I need to have about a dozen pwm channels for dimming, and am using 8 straight outputs and a few inputs as well. Looking to also control temp of the electrical enclosure with a temp sensor I already have, and two Noctua pwm fans. This part may or may not be taken care of though the solar system, not sure just yet as that would mean the fans would just be on/off, not pwm.

First of all, Xojo sucks with GPIO, the current support is more like a hack than a feature. Maybe it can be done but is not pretty. An easier way would be to create the “driver” using phyton to send/receive all the comunication and the UI in Xojo.

Then you can use a Shell object to interact with the phyton script. Or if you are more advanced in phyton, you can write a little server to keep it running and interact with your xojo app via sockets.

Sounds like the whole thing would be easier to just do in Python. I’m not looking to create some beautiful GUI. It’s honestly just On/Off and Sliders. At this point, it just needs to be functional. In my initial searching, Xojo was the easiest thing I could find that got me to a functioning GUI, but seems to lack in the back end.

Any suggestions for an IDE that I can at least make a simple GUI on? I’m not likely to spend hundreds of hours learning how to in depth become some kind of code master. I don’t have time or desire for that. Rather than bend and mold Xojo into something it isn’t, I’d rather just use something that IS what I want right out of the gate.

this would certainly be easier with an esp8266 or esp32 and an arduino sketch…
you can make it a web server, and buttons or sliders are made with html.
works here on my domotic home system like a charm for years…

2 Likes