Add fan speed sensor, add oil alarm motor cutout

master
Tom Wilson 4 years ago
parent af06bf73e4
commit 820f5843aa

@ -12,7 +12,9 @@
#define GPIO_WHEEL_SPEED GPIO_NUM_14
#define GPIO_OIL_PRESSURE GPIO_NUM_13
#define GPIO_FAN_SPEED GPIO_NUM_13
#define GPIO_OIL_PRESSURE GPIO_NUM_25
#define GPIO_BIN1_LEVEL GPIO_NUM_4
#define GPIO_BIN2_LEVEL GPIO_NUM_16

@ -11,13 +11,13 @@ that's been bundled out into its own FreeRTOS module (using the ESP IDF librarie
#include "bin_level.h"
#include "i2c_comms.h"
#include "io.h"
#include "wheel_speed.h"
#include "shaft_speed.h"
#define MOTOR_STATUS_UPDATE_PERIOD_MS 500
#define WHEEL_STATUS_UPDATE_PERIOD_MS 1000
#define BIN_STATUS_UPDATE_PERIOD_MS 3000 // also includes oil pressure sensor
#define PIPE_PRESSURE_UPDATE_PERIOD_MS 2000
#define HEARTBEAT_PERIOD_MS 1000
#define MOTOR_STATUS_UPDATE_PERIOD_MS 500
#define WHEEL_FAN_STATUS_UPDATE_PERIOD_MS 1000 // also includes oil pressure sensor
#define BIN_STATUS_UPDATE_PERIOD_MS 3000
#define PIPE_PRESSURE_UPDATE_PERIOD_MS 2000
#define HEARTBEAT_PERIOD_MS 1000
// Rate at which the current wheel speed will be used to update the desired roller speed
#define MOTOR_CONTROL_PERIOD_MS 100
@ -26,13 +26,13 @@ that's been bundled out into its own FreeRTOS module (using the ESP IDF librarie
#define POWER_NO_COMMS_TIMEOUT_MS 1000 * 60 * 5 // 5 minutes
EspMQTTClient mqtt_client("devtest", // Wifi SSID
"87usc6rs", // WiFi password
"10.9.2.101", // MQTT Broker server ip
"", // Can be omitted if not needed
"", // Can be omitted if not needed
"airseeder", // Client name that uniquely identify your device
1883 // The MQTT port, default to 1883. this line can be omitted
EspMQTTClient mqtt_client("RUT240_AC17", // Wifi SSID
"r8DBn0y4", // WiFi password
"192.168.1.1", // MQTT Broker server ip
"", // Can be omitted if not needed
"", // Can be omitted if not needed
"airseeder", // Client name that uniquely identify your device
1883 // The MQTT port, default to 1883. this line can be omitted
);
void setup() {
@ -67,6 +67,20 @@ void setup() {
Serial.println("Connected to MQTT broker");
}
// Oil pressure switch is active when oil pressure is low (and rollers should not be driven).
bool get_oil_pressure_alarm() { return (digitalRead(GPIO_OIL_PRESSURE) == LOW); }
// Bin empty photointerrupters are active when grain is blocking them.
bool get_bin_empty(bin_id_t b_id) {
if (b_id == BIN_1) {
return (digitalRead(GPIO_BIN1_EMPTY) == HIGH);
}
if (b_id == BIN_2) {
return (digitalRead(GPIO_BIN2_EMPTY) == HIGH);
}
return (digitalRead(GPIO_BIN3_EMPTY) == HIGH);
}
char msg_buffer[100];
#define MAX_MSG_SEGMENTS 7
@ -324,28 +338,28 @@ void loop() {
mqtt_client.publish("airseeder/status/pipepressure", msg_buffer, true);
}
// Periodically publish the wheel status
if (now - last_wheel_status > WHEEL_STATUS_UPDATE_PERIOD_MS) {
// Periodically publish the wheel and fan status
if (now - last_wheel_status > WHEEL_FAN_STATUS_UPDATE_PERIOD_MS) {
last_wheel_status = now;
sprintf(msg_buffer, "%lu %.2f %.2f", now, get_wheel_speed(), get_wheel_revs());
mqtt_client.publish("airseeder/status/wheel", msg_buffer, true);
sprintf(msg_buffer, "%lu %.2f %d", now, get_fan_speed(), get_oil_pressure_alarm());
mqtt_client.publish("airseeder/status/fan", msg_buffer, true);
}
// `airseeder/status/bin1` - `[timestamp] [empty] [distance]
// Periodically publish the bin status
if (now - last_bin_status > BIN_STATUS_UPDATE_PERIOD_MS) {
last_bin_status = now;
sprintf(msg_buffer, "%lu %d %.2f", now, digitalRead(GPIO_BIN1_EMPTY), get_bin_level(BIN_1));
sprintf(msg_buffer, "%lu %d %.2f", now, get_bin_empty(BIN_1), get_bin_level(BIN_1));
mqtt_client.publish("airseeder/status/bin1", msg_buffer, true);
sprintf(msg_buffer, "%lu %d %.2f", now, digitalRead(GPIO_BIN2_EMPTY), get_bin_level(BIN_2));
sprintf(msg_buffer, "%lu %d %.2f", now, get_bin_empty(BIN_2), get_bin_level(BIN_2));
mqtt_client.publish("airseeder/status/bin2", msg_buffer, true);
sprintf(msg_buffer, "%lu %d %.2f", now, digitalRead(GPIO_BIN3_EMPTY), get_bin_level(BIN_3));
sprintf(msg_buffer, "%lu %d %.2f", now, get_bin_empty(BIN_3), get_bin_level(BIN_3));
mqtt_client.publish("airseeder/status/bin3", msg_buffer, true);
sprintf(msg_buffer, "%lu %d", now, digitalRead(GPIO_OIL_PRESSURE));
mqtt_client.publish("airseeder/status/oilpressure", msg_buffer, true);
}
// Periodically publish the heartbeat
@ -409,6 +423,12 @@ void loop() {
m_ctrl[r_id].enabled = false;
m_ctrl[r_id].desired_speed = 0.0;
}
// If we don't have oil pressure yet, do not actually drive motors (but still allow speed setting and mode
// changes)
if (get_oil_pressure_alarm()) {
m_ctrl[r_id].enabled = false;
}
}
motor_control_group.motor_1 = m_ctrl[0];
motor_control_group.motor_2 = m_ctrl[1];

@ -1,6 +1,6 @@
// Module to record roller and wheel speeds from MCPWM capture interrupts. Not yet sure if a task is required at all.
#include "wheel_speed.h"
#include "shaft_speed.h"
#include "io.h"
#include "driver/mcpwm.h"
@ -10,11 +10,14 @@
static uint32_t wheel_pulses = 0; // Total pulses of wheel so far
static uint32_t wheel_last_edge_time = 0; // Precision timer value of most recent edge
static uint32_t fan_last_edge_time = 0;
// Also store timestamp as microseconds since start to avoid overflow of the precision timer when checking for stop
static int64_t wheel_last_timestamp = 0;
static int64_t fan_last_timestamp = 0;
// Use a single item queue for each measurement we need to distribute.
static xQueueHandle wheel_measurement_queue;
static xQueueHandle fan_measurement_queue;
// struct to hold move our pulse measurements from our ISR via the queue
typedef struct {
@ -30,32 +33,43 @@ static bool speed_pulse_isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id
// Not strictly necessary to do the wakeup check here, as we shouldn't be blokcing anything. Doesn't hurt though.
BaseType_t high_task_wakeup = pdFALSE;
if (cap_sig != MCPWM_SELECT_CAP0) {
// Not sure how we'd ever wind up here
return false;
}
// Add one to our revs counter
wheel_pulses++;
pulse_measurement_t new_measurement;
int64_t time_now;
time_now = esp_timer_get_time();
// Just make sure we have a previous edge to compare to, and that it was no more than ZERO_SPEED_TIMEOUT_US ago
if ((wheel_last_timestamp != 0) and ((time_now - wheel_last_timestamp) < ZERO_SPEED_TIMEOUT_US)) {
new_measurement.timestamp = time_now;
new_measurement.period = edata->cap_value - wheel_last_edge_time;
// Like with the rollers, technically passing the wheel pulses with the rest of the measurement here means that it won't be updated
// for the first pulse detected after being stopped. The total count will still be correct, but will just not update for 1.
new_measurement.pulses = wheel_pulses;
xQueueOverwriteFromISR(wheel_measurement_queue, &new_measurement, &high_task_wakeup);
if (cap_sig == MCPWM_SELECT_CAP0){
// Add one to our revs counter
wheel_pulses++;
time_now = esp_timer_get_time();
// Just make sure we have a previous edge to compare to, and that it was no more than ZERO_SPEED_TIMEOUT_US ago
if ((wheel_last_timestamp != 0) and ((time_now - wheel_last_timestamp) < ZERO_SPEED_TIMEOUT_US)) {
new_measurement.timestamp = time_now;
new_measurement.period = edata->cap_value - wheel_last_edge_time;
// Like with the rollers, technically passing the wheel pulses with the rest of the measurement here means that it won't be updated
// for the first pulse detected after being stopped. The total count will still be correct, but will just not update for 1.
new_measurement.pulses = wheel_pulses;
xQueueOverwriteFromISR(wheel_measurement_queue, &new_measurement, &high_task_wakeup);
}
wheel_last_edge_time = edata->cap_value;
wheel_last_timestamp = time_now;
} else if (cap_sig == MCPWM_SELECT_CAP1){
time_now = esp_timer_get_time();
// Just make sure we have a previous edge to compare to, and that it was no more than ZERO_SPEED_TIMEOUT_US ago
if ((fan_last_timestamp != 0) and ((time_now - fan_last_timestamp) < ZERO_SPEED_TIMEOUT_US)) {
new_measurement.timestamp = time_now;
new_measurement.period = edata->cap_value - fan_last_edge_time;
xQueueOverwriteFromISR(fan_measurement_queue, &new_measurement, &high_task_wakeup);
}
fan_last_edge_time = edata->cap_value;
fan_last_timestamp = time_now;
} else {
// Not sure how we'd ever wind up here
return false;
}
wheel_last_edge_time = edata->cap_value;
wheel_last_timestamp = time_now;
return high_task_wakeup == pdTRUE;
}
@ -71,6 +85,18 @@ float get_wheel_speed(){
return ((float)rtc_clk_apb_freq_get() / (float)(pulse_measurement.period)) / (float)(WHEEL_PULSES_PER_REV);
}
float get_fan_speed(){
pulse_measurement_t pulse_measurement;
// Get the pulse period in APB clock ticks.
xQueuePeek(fan_measurement_queue, &pulse_measurement, 0);
if ((pulse_measurement.period == 0) or
((esp_timer_get_time() - pulse_measurement.timestamp) >= ZERO_SPEED_TIMEOUT_US)) {
return 0.0;
}
// Convert that to revs/sec
return ((float)rtc_clk_apb_freq_get() / (float)(pulse_measurement.period)) / (float)(FAN_PULSES_PER_REV);
}
float get_wheel_revs() {
pulse_measurement_t pulse_measurement;
xQueuePeek(wheel_measurement_queue, &pulse_measurement, 0);
@ -80,18 +106,23 @@ float get_wheel_revs() {
void wheel_speed_setup(void) {
// Create our measurement queue
wheel_measurement_queue = xQueueCreate(1, sizeof(pulse_measurement_t));
fan_measurement_queue = xQueueCreate(1, sizeof(pulse_measurement_t));
// Prime queue
pulse_measurement_t blank_measurement = {0, 0};
xQueueOverwrite(wheel_measurement_queue, &blank_measurement);
xQueueOverwrite(fan_measurement_queue, &blank_measurement);
// Attach GPIO to capture unit 0 in MCPWM0
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPIO_WHEEL_SPEED);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_FAN_SPEED);
// Configure and enable MCPWM capture unit
mcpwm_capture_config_t conf = {.cap_edge = MCPWM_POS_EDGE, // trigger on riding edge
.cap_prescale = 1, // pulses per interrupt
.capture_cb = speed_pulse_isr_handler,
.user_data = NULL};
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf);
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, &conf);
}

@ -1,10 +1,11 @@
#ifndef wheel_speed_h
#define wheel_speed_h
#ifndef shaft_speed_h
#define shaft_speed_h
// Number of sensor pulses per wheel rev. Note that this will overflow at about 4 billion pulses
#define WHEEL_PULSES_PER_REV 40
#define FAN_PULSES_PER_REV 1
// Time without a pulse after which the speed will be considered to be 0.
// Note that with a clock at 80MHz, the pulse period counter overflows every 53 seconds
#define ZERO_SPEED_TIMEOUT_US 1000000
@ -14,6 +15,8 @@ void wheel_speed_setup(void);
// Returns the last calculated speed for the wheel in revs/second.
float get_wheel_speed();
float get_fan_speed();
// Returns the total rev count for the wheel.
float get_wheel_revs();
Loading…
Cancel
Save