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_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_BIN1_LEVEL GPIO_NUM_4
#define GPIO_BIN2_LEVEL GPIO_NUM_16 #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 "bin_level.h"
#include "i2c_comms.h" #include "i2c_comms.h"
#include "io.h" #include "io.h"
#include "wheel_speed.h" #include "shaft_speed.h"
#define MOTOR_STATUS_UPDATE_PERIOD_MS 500 #define MOTOR_STATUS_UPDATE_PERIOD_MS 500
#define WHEEL_STATUS_UPDATE_PERIOD_MS 1000 #define WHEEL_FAN_STATUS_UPDATE_PERIOD_MS 1000 // also includes oil pressure sensor
#define BIN_STATUS_UPDATE_PERIOD_MS 3000 // also includes oil pressure sensor #define BIN_STATUS_UPDATE_PERIOD_MS 3000
#define PIPE_PRESSURE_UPDATE_PERIOD_MS 2000 #define PIPE_PRESSURE_UPDATE_PERIOD_MS 2000
#define HEARTBEAT_PERIOD_MS 1000 #define HEARTBEAT_PERIOD_MS 1000
// Rate at which the current wheel speed will be used to update the desired roller speed // Rate at which the current wheel speed will be used to update the desired roller speed
#define MOTOR_CONTROL_PERIOD_MS 100 #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 #define POWER_NO_COMMS_TIMEOUT_MS 1000 * 60 * 5 // 5 minutes
EspMQTTClient mqtt_client("devtest", // Wifi SSID EspMQTTClient mqtt_client("RUT240_AC17", // Wifi SSID
"87usc6rs", // WiFi password "r8DBn0y4", // WiFi password
"10.9.2.101", // MQTT Broker server ip "192.168.1.1", // MQTT Broker server ip
"", // Can be omitted if not needed "", // Can be omitted if not needed
"", // Can be omitted if not needed "", // Can be omitted if not needed
"airseeder", // Client name that uniquely identify your device "airseeder", // Client name that uniquely identify your device
1883 // The MQTT port, default to 1883. this line can be omitted 1883 // The MQTT port, default to 1883. this line can be omitted
); );
void setup() { void setup() {
@ -67,6 +67,20 @@ void setup() {
Serial.println("Connected to MQTT broker"); 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]; char msg_buffer[100];
#define MAX_MSG_SEGMENTS 7 #define MAX_MSG_SEGMENTS 7
@ -324,28 +338,28 @@ void loop() {
mqtt_client.publish("airseeder/status/pipepressure", msg_buffer, true); mqtt_client.publish("airseeder/status/pipepressure", msg_buffer, true);
} }
// Periodically publish the wheel status // Periodically publish the wheel and fan status
if (now - last_wheel_status > WHEEL_STATUS_UPDATE_PERIOD_MS) { if (now - last_wheel_status > WHEEL_FAN_STATUS_UPDATE_PERIOD_MS) {
last_wheel_status = now; last_wheel_status = now;
sprintf(msg_buffer, "%lu %.2f %.2f", now, get_wheel_speed(), get_wheel_revs()); sprintf(msg_buffer, "%lu %.2f %.2f", now, get_wheel_speed(), get_wheel_revs());
mqtt_client.publish("airseeder/status/wheel", msg_buffer, true); 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] // `airseeder/status/bin1` - `[timestamp] [empty] [distance]
// Periodically publish the bin status // Periodically publish the bin status
if (now - last_bin_status > BIN_STATUS_UPDATE_PERIOD_MS) { if (now - last_bin_status > BIN_STATUS_UPDATE_PERIOD_MS) {
last_bin_status = now; 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); 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); 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); 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 // Periodically publish the heartbeat
@ -409,6 +423,12 @@ void loop() {
m_ctrl[r_id].enabled = false; m_ctrl[r_id].enabled = false;
m_ctrl[r_id].desired_speed = 0.0; 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_1 = m_ctrl[0];
motor_control_group.motor_2 = m_ctrl[1]; 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. // 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 "io.h"
#include "driver/mcpwm.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_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 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 // 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 wheel_last_timestamp = 0;
static int64_t fan_last_timestamp = 0;
// Use a single item queue for each measurement we need to distribute. // Use a single item queue for each measurement we need to distribute.
static xQueueHandle wheel_measurement_queue; static xQueueHandle wheel_measurement_queue;
static xQueueHandle fan_measurement_queue;
// struct to hold move our pulse measurements from our ISR via the queue // struct to hold move our pulse measurements from our ISR via the queue
typedef struct { 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. // 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; 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; pulse_measurement_t new_measurement;
int64_t time_now; int64_t time_now;
time_now = esp_timer_get_time(); if (cap_sig == MCPWM_SELECT_CAP0){
// Just make sure we have a previous edge to compare to, and that it was no more than ZERO_SPEED_TIMEOUT_US ago // Add one to our revs counter
if ((wheel_last_timestamp != 0) and ((time_now - wheel_last_timestamp) < ZERO_SPEED_TIMEOUT_US)) { wheel_pulses++;
new_measurement.timestamp = time_now;
new_measurement.period = edata->cap_value - wheel_last_edge_time; time_now = esp_timer_get_time();
// Like with the rollers, technically passing the wheel pulses with the rest of the measurement here means that it won't be updated // Just make sure we have a previous edge to compare to, and that it was no more than ZERO_SPEED_TIMEOUT_US ago
// for the first pulse detected after being stopped. The total count will still be correct, but will just not update for 1. if ((wheel_last_timestamp != 0) and ((time_now - wheel_last_timestamp) < ZERO_SPEED_TIMEOUT_US)) {
new_measurement.pulses = wheel_pulses; new_measurement.timestamp = time_now;
xQueueOverwriteFromISR(wheel_measurement_queue, &new_measurement, &high_task_wakeup); 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; 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); 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() { float get_wheel_revs() {
pulse_measurement_t pulse_measurement; pulse_measurement_t pulse_measurement;
xQueuePeek(wheel_measurement_queue, &pulse_measurement, 0); xQueuePeek(wheel_measurement_queue, &pulse_measurement, 0);
@ -80,18 +106,23 @@ float get_wheel_revs() {
void wheel_speed_setup(void) { void wheel_speed_setup(void) {
// Create our measurement queue // Create our measurement queue
wheel_measurement_queue = xQueueCreate(1, sizeof(pulse_measurement_t)); wheel_measurement_queue = xQueueCreate(1, sizeof(pulse_measurement_t));
fan_measurement_queue = xQueueCreate(1, sizeof(pulse_measurement_t));
// Prime queue // Prime queue
pulse_measurement_t blank_measurement = {0, 0}; pulse_measurement_t blank_measurement = {0, 0};
xQueueOverwrite(wheel_measurement_queue, &blank_measurement); xQueueOverwrite(wheel_measurement_queue, &blank_measurement);
xQueueOverwrite(fan_measurement_queue, &blank_measurement);
// Attach GPIO to capture unit 0 in MCPWM0 // 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_0, GPIO_WHEEL_SPEED);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_FAN_SPEED);
// Configure and enable MCPWM capture unit // Configure and enable MCPWM capture unit
mcpwm_capture_config_t conf = {.cap_edge = MCPWM_POS_EDGE, // trigger on riding edge mcpwm_capture_config_t conf = {.cap_edge = MCPWM_POS_EDGE, // trigger on riding edge
.cap_prescale = 1, // pulses per interrupt .cap_prescale = 1, // pulses per interrupt
.capture_cb = speed_pulse_isr_handler, .capture_cb = speed_pulse_isr_handler,
.user_data = NULL}; .user_data = NULL};
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf); 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 #ifndef shaft_speed_h
#define wheel_speed_h #define shaft_speed_h
// Number of sensor pulses per wheel rev. Note that this will overflow at about 4 billion pulses // Number of sensor pulses per wheel rev. Note that this will overflow at about 4 billion pulses
#define WHEEL_PULSES_PER_REV 40 #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. // 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 // Note that with a clock at 80MHz, the pulse period counter overflows every 53 seconds
#define ZERO_SPEED_TIMEOUT_US 1000000 #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. // Returns the last calculated speed for the wheel in revs/second.
float get_wheel_speed(); float get_wheel_speed();
float get_fan_speed();
// Returns the total rev count for the wheel. // Returns the total rev count for the wheel.
float get_wheel_revs(); float get_wheel_revs();
Loading…
Cancel
Save