Minor Trajectory Change
Originally, I intended to develop the OpenGlow’s drivers and control software independently, and add a compatibility layer for the GFUI later. This approach was largely driven by the expectation that GF’s release of code was not likely to happen soon. With the release of Glowforge’s kernel modules, I’ve opted to change course a bit.
The sooner I can get a functioning system into the wild, the sooner there will be opportunity for others to join the development effort. With that in mind, I am going to focus on making the board work with the existing GFUI first, leveraging the drivers that GF has released.
Thermal Subsystem
I began with the thermal module. This component provides the kernel modules that interface with the intake and exhaust fans, coolant pump, thermoelectric cooler, and a water heater used to detect coolant flow. The full source commit can be viewed here.
I packaged the module in the kernel-module-openglow
Yocto recipe located in the meta-openglow\openglow-recipes
layer. This produces the openglow.ko
module which automatically loads at boot:
openglow: loading out-of-tree module taints kernel.
openglow_init: started
openglow_thermal openglow-thermal: thermal_probe: started
openglow_thermal openglow-thermal: thermal_probe: done
openglow_init: done
As other components are added to the module (i.e. pic, cnc, etc…), they will load here as part of the module’s init process.
Once loaded, the module creates the following sysfs
entries to expose the api to user space:
root@openglow_std:/sys/openglow# ls
thermal
root@openglow_std:/sys/openglow# ls thermal
driver heater_pwm of_node tach_exhaust tec_on
driver_override intake_pwm power tach_intake_1 uevent
exhaust_pwm modalias subsystem tach_intake_2 water_pump_on
root@openglow_std:/sys/openglow#
User API
There are three PWM output controls, three tachometer inputs, and two GPIO outputs.
PWM Outputs
Hardware PWM’s
The cooling fans are controlled with standard 25 kHz (fixed frequency) PWM outputs, with the two intake fans sharing a common output. These outputs are generated by two of the hardware PWM modules in the SOC. The duty cycle is set by writing a value from 0 - 65535 corresponding to a range of 0 - 100%.
The Glowforge code is based on the legacy PWM interface. To work with the newer kernel, I had to do a little modification, and I will be updating it to the use the modern methods in the near future. This will include changing the control values to be simple percentages instead of the 16 bit integer.
The following sets the exhaust fan to 100% (EFrd: 0xFFFF
), and the intake fans to 66% (IFrd: 0xA90E
), which is the standard configuration for a program run:
root@openglow_std:/sys/openglow/thermal# echo 65535 >> exhaust_pwm
root@openglow_std:/sys/openglow/thermal# echo 43278 >> intake_pwm
root@openglow_std:/sys/openglow#
Software PWM
To detect coolant flow, the Glowforge uses two temperature sensors and a heater. One sensor is positioned upstream of the heater, and the other downstream. Theory: By slightly heating the coolant, there will be a detectable difference in the upstream and downstream coolant temperatures - when the coolant is flowing.
I haven’t fully explored this yet. It is also possible that the heater is only used for a short time with the pump off. It would be powered until a temperature rise is detected, at which time powering the pump would quickly drop that temperature indicating that coolant is flowing. This seems more reasonable to me.
The Freescale iMX6 SOC has 4 hardware PWMs. On the Glowforge factory control board, two are used for the cooling fans, one is used for the laser power, and one is used for the speaker. They needed a fifth to control the heater, so they created a software PWM that switches a standard GPIO on and off. This is controlled by writing the desired duty cycle (same values as the cooling fans) to the heater_pwm
node.
During the design of the OpenGlow hardware, I was unaware that the heater output was being used as a software PWM, so I assigned it a standard GPIO. However, I chose not to implement the speaker (it seemed silly), leaving the fourth hardware PWM free. It is likely that I will asssign this to the heater in the next hardware revision, freeing up the interrupt/CPU overhead that is currently chewed up by the software PWM.
Fan Tachometer Inputs
Each fan has a tachometer output that is connected to a standard GPIO input, which receives 2 pulses for each fan revolution. The kernel module sets an interrupt to occur on the rising edge of each of these pulses. The interrupt handler records the time between each pulse, and this is available to the user by reading the value tach_exhaust
, tach_intake_1
, and tach_intake_2
. With the PWM values as set in the previous examples, we get readings similiar to these:
root@openglow_std:/sys/devices/soc0/openglow-thermal# cat tach_exhaust
2434666
root@openglow_std:/sys/devices/soc0/openglow-thermal# cat tach_intake_1
7143334
root@openglow_std:/sys/devices/soc0/openglow-thermal# cat tach_intake_2
7193334
The values are in nanoseconds, and with a little math ( RPM = 60 / ((tach_ns * 2) / 1,000,000,000) ) we can get the RPM values:
EXHAUST: 12,322 RPM
INTAKE 1: 4,200 RPM
INTAKE 2: 4,170 RPM
The interrupt handler has a feature whereby it is smart enough to know when the fans have stopped, so the timer will not keep incrementing forever (and rolling over back to zero).
A future hardware change I am considering is to add a dedicated fan controller that will unload the tachometer duties from the CPU.
General Purpose Outputs
We are also given control over two outputs: the coolant pump, and the thermoelectric cooler (TEC). By writing a 0 (OFF) or a 1(ON) to either of the nodes, we control their state. We can also read the values to determine what their current state is.
For instance, shutting off the coolant pump:
root@openglow_std:/sys/devices/soc0/openglow-thermal# cat water_pump_on
1
root@openglow_std:/sys/devices/soc0/openglow-thermal# echo 0 > water_pump_on
root@openglow_std:/sys/devices/soc0/openglow-thermal# cat water_pump_on
0
In normal operation, the Glowforge factory default state upon power up has the fans and TEC off and the coolant pump on. These are actually set by the bootloader (U-Boot) to ensure it happens even if the system fails to boot its operating system or the kernel module fails to load. I’ll be following this convention with the OpenGlow.
Next Steps
I’m currently porting over the PIC module code to work with the OpenGlow. Both the factory and OpenGlow boards utilize a PIC microcontroller as an analog IO controller - providing 4 PWM outpus, 2 DAC outputs, and numerous ADC inputs. The factory Glowforge uses the SPI bus to communicate with the PIC, while the OpenGlow uses I2C. Once this component is complete, the user space API to the LED’s, HV voltage/current, and numerous temperature sensors will be in place. After that, I’ll move on to the CNC module which controls all motion and laser functions.