- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我无法弄清楚如何禁用然后重新启用(在触发事件时)来自 esp-arduino 库的 hw (esp32-hal-timer) 计时器,here用于我的 esp32 开发板的步进电机 Controller 应用程序。它会倒计时并根据需要多次触发 ISR,但是当我禁用它时(这样 ISR 就不会被不必要地调用),当我再次尝试启动它时它不会启动。奇怪的是它以与第一次相同的方式启动,所以我不确定这是我的代码问题还是特定库处理垃圾收集的方式。这也是我第一次尝试使用中断。我的代码如下。
为了避免折腾太多,一般流程是在setup方法中初始化timer(称为motorTimer),然后连接wifi,在mqtt的回调方法中,任何payload为整数的消息都会触发motor.h 类中的“moveTo”方法,然后在触发 ISR 计时器时将触发同一类中的更新方法。然后定时器将在每次迭代中更改其时间,以进行加速度补偿。这很好用,直到需要终止计时器然后稍后重新启动它 - 然后根本不会调用 ISR,就像计时器没有正确停止一样。这就是我的问题所在。
#include <SPI.h>
#include <Adafruit_MAX31855.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
// Replace the next variables with your SSID/Password combination
// //WiFi info:
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
// Add your MQTT Broker IP address, example:
const char* mqtt_server = "home.IOT.lan";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
// get size of array first, to feed to for loop
int numDevices = sizeof(devices)/sizeof(device);
int value = 0;
unsigned long heartbeat_previousMillis = 0;
unsigned long motorCheck_previousMillis = 0;
const long timeOut = 60000;
const long motorCheckTime = 600;
hw_timer_t * motorTimer = NULL;
bool state = 0;
int count = 0;
int d = 0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
bool finished = false;
enum LogLevel
{
Debug, //Sends message only to the serial port
Error, //Sends message over MQTT to 'Errors' topic, and to serial port with "Error: " pre-appended
Message //Sends message over serial and MQTT to 'StatusMessage' topic
};
///****** TIMER LOGIC HERE ******
//TODO: timer needs to just figure out what time the next pulse needs to be fired at - needs to be calculated on the fly
//This is calculated inside the Motor class.
void IRAM_ATTR motorInterrupt(void)
{
Serial.println("B");
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor.getMotorStatus())
{
d = linMotor.Update();
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
else
{
// timerAlarmWrite(motorTimer, 1, true);
timerAlarmDisable(motorTimer);
finished = true;
}
//kill the timer and interrupt if not
interrupts();
portEXIT_CRITICAL(&timerMux);
}
//****** END TIMER HERE *****
void log(LogLevel level, String message)
{
switch(level)
{
case LogLevel::Debug:
Serial.println(message);
break;
case LogLevel::Error:
print(ErrorTopic, message);
break;
case LogLevel::Message:
Serial.print("Message: ");
Serial.println(message);
print(StatusTopic, message);
break;
}
}
void print(char topic[], String message)
{
Serial.print(topic);
Serial.print(" : ");
Serial.println(message);
//topic.toCharArray(topArr, sizeof(topic)+2);
message.toCharArray(msgArr, sizeof(msgArr));
client.publish(topic, msgArr, message.length());
}
// WiFi methods are located below:
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
//check if heartbeat signal was sent
if (String(topic) == HeartbeatTopic)
{
heartbeat_previousMillis = millis();
}
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
// Iterate through each device and update states accordingly
for (int i = 0; i < numDevices; i = i + 1)
{
// the char arrays need to be cast as strings to compare to each other
if (String(topic) == String(devices[i].controlTopic))
{
if (messageTemp == "on")
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?LOW:HIGH);
response = "on";
}
else
{
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
response = "off";
}
log(LogLevel::Message, response);
print(devices[i].stateTopic, response);
break;
}
if (String(topic) == String(motorControlTopic) || String(topic) == String(motorSetTopic))
{
if (String(topic) == String(motorSetTopic))
{
//sets speed for now, other params later
linMotor.SetSpeed(messageTemp.toInt());
response = "Parameters set";
}
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor.getMotorStatus())
{
linMotor.MoveTo(messageTemp.toInt());
//TODO: Setup timer stuff here
// motorTimer = NULL;
// motorTimer = timerBegin(1, 80, true);
// timerAttachInterrupt(motorTimer, &motorInterrupt, true);
timerSetAutoReload(motorTimer, true);
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
}
else if (messageTemp == "home")
{
linMotor.SetZero();
response = "setting motor to zero";
}
else if (messageTemp == "stop")
{
linMotor.EStop();
if (motorTimer != NULL)
{
timerDetachInterrupt(motorTimer);
}
response = "motor stopped";
//TODO: detach timer here!
}
}
//TODO: put in GUI call for position updates
//print(motorStateTopic, "position is: " + String(linMotor.getPosition()));
log(LogLevel::Message, response);
print(motorStateTopic, response);
break;
}
}
}
void setup()
{
//Testing code here:
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
//end testing code
//Start serial connection
Serial.begin(115200);
for (int i = 0; i < numDevices; i = i + 1)
{
pinMode(devices[i].pinNumber, OUTPUT);
digitalWrite(devices[i].pinNumber, devices[i].shouldInvert?HIGH:LOW);
}
pinMode(pulsePin, OUTPUT);
pinMode(directionPin, OUTPUT);
delay(500);
linMotor.SetSpeed(250);
linMotor.SetAcceleration(20);
log(LogLevel::Debug, "Connecting to mqtt");
Serial.println(mqtt_server);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
reconnect();
log(LogLevel::Message, "Connected");
log(LogLevel::Message, "System Started!");
// Initialize heartbeat timer
heartbeat_previousMillis = millis();
motorCheck_previousMillis = millis();
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial.println("connected");
// Subscribe to all relevant messages
for (int i = 0; i < numDevices; i = i + 1)
{
client.subscribe(devices[i].controlTopic);
}
// subscribe to the heartbeat topic as well
client.subscribe(HeartbeatTopic);
client.subscribe(motorControlTopic);
client.subscribe(motorSetTopic);
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
unsigned long currentMillis = millis();
if (currentMillis - motorCheck_previousMillis >= motorCheckTime)
{
// print(motorStateTopic, "position is: " + String(linMotor.GetPosition()));
portENTER_CRITICAL(&timerMux);
Serial.println(String(linMotor.GetPosition()));
portEXIT_CRITICAL(&timerMux);
motorCheck_previousMillis = currentMillis;
if (finished)
{
// timerAlarmWrite(motorTimer, 0, false);
// Serial.println("wrote 0 alarm");
// timerAlarmDisable(motorTimer); // stop alarm
// Serial.println("disabled alarm");
// timerEnd(motorTimer);
// Serial.println("timerEnd");
// motorTimer = NULL;
// Serial.println("NULLED timer");
// motorTimer = timerBegin(1, 80, true);
// Serial.println("timer stated again!");
// timerRestart(motorTimer);
// timerDetachInterrupt(motorTimer); // detach interrupt
// timerEnd(motorTimer);
finished = false;
}
}
}
我认为不需要显示 motor.h 方法,但如果需要可以发布。在此先感谢您的帮助!
Edit1:刚刚意识到在循环函数内部发生定时器删除之前互斥量已关闭,但它仍然没有修复它。该部分评论中的内容也是我迄今为止尝试但未成功的所有内容。
Edit2:为清楚起见重新措辞。
最佳答案
“重启”这个词让我以为它会立即再次启动计时器,但事实并非如此。如果之前将重新加载设置为 false,则必须在计时器实际执行之前再次设置计时器 - 这非常适合我的用例。下面是我的新代码(我想我会包括 wifi 和 mqtt 的东西来帮助其他人):
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include "VCDevices.h"
hw_timer_t * motorTimer = NULL;
Motor linMotor2 = Motor(pulsePin, directionPin);
int d = 0;
bool nextRun = false;
const char* ssid = "SSID";
const char* password = "PASSWORD_HERE";
const char* mqtt_server = "MQTT_SERVER_HERE";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char topArr[50];
char msgArr[100];
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
//Timer ISR
void IRAM_ATTR motorInterrupt(void)
{
portENTER_CRITICAL(&timerMux);
noInterrupts();
//check if the motor is in motion still
if (!linMotor2.getMotorStatus())
{
d = linMotor2.Update();
//give timer different delay, dependent on its current speed
timerAlarmWrite(motorTimer, d, true);
timerAlarmEnable(motorTimer);
}
//kill the timer and interrupt if not
else
{
nextRun = true;
//set the 'reload' boolean to false, to get it to only trigger one more time
timerAlarmWrite(motorTimer, 10, false);
// Serial.println("POSITION REACHED!");
}
interrupts();
portEXIT_CRITICAL(&timerMux);
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected())
{
Serial.print("Attempting MQTT connection...");
Serial.println(mqtt_server);
// Attempt to connect
if (client.connect("ESP8266Client"))
{
client.subscribe(motorControlTopic);
Serial.println("connected");
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length)
{
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
String response;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
if (String(topic) == String(motorControlTopic))
{
if (messageTemp.toInt() > 0)
{
//check if motor is available to run
if (linMotor2.getMotorStatus())
{
linMotor2.MoveTo(messageTemp.toInt());
//set the motor timer and enable it
timerAlarmWrite(motorTimer, 1, true);
timerAlarmEnable(motorTimer);
response = "moving to " + String(messageTemp.toInt()) + " mm position";
}
else
{
response = "motor is busy - wait for movement to end!";
}
Serial.println(response);
}
}
}
void setup()
{
//Start serial connection
Serial.begin(115200);
//Setup wifi and mqtt stuff
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
//Fix up motor settings
pinMode(pulsePin, OUTPUT);
linMotor2.SetSpeed(200);
linMotor2.SetAcceleration(10);
//Initialize timer here for later use!
motorTimer = timerBegin(1, 80, true);
timerAttachInterrupt(motorTimer, &motorInterrupt, true);
Serial.println("TIMER SET!!");
digitalWrite(pulsePin, LOW);
}
void loop()
{
if (!client.connected()) reconnect();
client.loop();
portENTER_CRITICAL(&timerMux);
vTaskDelay(500);
count = linMotor2.GetPosition();
Serial.println("POSITION: " + String(count));
if (nextRun)
{
noInterrupts();
timerRestart(motorTimer);
Serial.println("*********TIMER RESTARTED!******");
nextRun = false;
interrupts();
}
portEXIT_CRITICAL(&timerMux);
}
关于timer - 如何按需删除和重启 esp32 arduino(步进电机 Controller 应用程序)的硬件定时器(用于中断),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64657661/
Yii::$app->runAction('new_controller/new_action', $params); 我相信这可以用来从另一个 Controller 调用 Controller Ac
这个问题类似于 this ,但我需要访问父成员(不是控制)。我不知道是否可以不使用依赖注入(inject)。 例如,我有一个父级,有一个成员调用用户,我需要从子 Controller 访问用户。 最佳
我有包含 2 个布局的根布局:- 选项面板- 绘制区域 我正在尝试的是访问 OptionsPaneController 中的 DrawAreaController 以调用其绘制方法。下面是 Optio
我的应用程序的 View Controller 层次结构设置如下: UIViewController | UITabBarController | UINavigationCo
我的应用程序的 View Controller 层次结构设置如下: UITabBarController | UINavigationController | | |
当我第一次为我目前在 Storyboard 中开发的应用程序创建基础布局时,我分两步完成: 选择我的 View Controller 并使用 Editor->Embed In->Navigation
设计要求: 显示用户可以选择的项目列表 选择一个项目后,使用后退按钮将用户带到一个新 View 。新 View 应在底部包含第一个屏幕中不存在的选项卡列表 单击选项卡中的项目时,应出现一个带有后退按钮
将父 Controller 设置为“parentCtrl as vm”,并将子 Controller 设置为“childCtrl as vmc”,以避免名称冲突,并且效果良好。 如何在子 Contro
我已经阅读了一些答案,例如关闭当前的 ViewController,但我的情况有所不同,因为我正在展示另一个 ViewController。 虽然我无法访问它的属性,但此代码显示了带有导航 Contr
如我所见,如果我们要实例化一个Model(例如,名为Post),我们只需调用: $post = new Post(); 现在,我还想实例化一个Controller(例如,名为Post,并为此 Cont
我已经疯狂地在整个网络上搜索解决我的问题的方法,但目前还没有。我的问题是我必须检查是否在 HTTP 请求中获得特定文本,该请求在一个 while 循环中,如果我这样做了,那么我应该离开循环并继续线程,
我想用this.get('controllers.pack.query');要得到App.PackQueryController在 App.PackController ,但失败了。 我认为问题是 E
我刚开始使用 Laravel。当我使用 codeigniter 或 zend 框架时,我可以将我的 Controller 组织到一个单独的目录中。例如,我可以创建“user/permission.ph
在 emberjs 前 2 我们可以从另一个 Controller 访问 Controller 或 Controller 中的任何方法 以下方式: App.get('router').get('nav
这可能是非常简单的实现,但我是 iOS 编程的新手,我似乎被卡住了。 所以,基本上,我有一个选项卡式应用程序。我决定除了标签栏之外还需要一个导航栏。为此,我放置了标签栏 Controller ,然后添
我有这个列表 Controller , define([ 'jquery', 'app' ], function ($,app) { app.controller("ListC
我有 3 个 Controller :RootController、FirstController 和 SecondController。我想从 RootController -> FirstCont
我有以下 Controller : /controllers/api/base_controller.rb /controllers/api/v1/articles_controller.rb 当为文
我是 Angular JS 的新手,尝试在另一个 Controller 中调用一个 Controller ,但出现以下错误。 ionic.bundle.js:21157 TypeError: $con
我有一个标签栏 Controller 和它的 3 个 child ,我还有另一个 View ,我制作了一个从 child 到 View Controller 的自定义转场,还有一个从 View Con
我是一名优秀的程序员,十分优秀!