sinricpro / esp8266-esp32-sdk

Library for https://sinric.pro - simple way to connect your device to Alexa, Google Home, SmartThings and cloud
https://sinric.pro
228 stars 122 forks source link

How do I control a stepper motor with the switch example? #208

Closed xplusten closed 2 years ago

xplusten commented 2 years ago

Sinric connects fine to Alexa when using switch example which turns off and on the in-built LED.

But how would I change the switch code to make it move a motor clockwise and counter clickwise?

From the switch example. I want to be able to use basic stepper motor function, (the stepper library)

thank you

sivar2311 commented 2 years ago

Hi @xplusten !

You can do it like so:

bool onPowerState(const String &deviceId, bool &state) {
    if (state) {
        stepper.step(100); // turn on: make 100 steps in positive direction
    } else {
        stepper.step(-100);  // turn off: make 100 steps in negative direction
    }
    return true;
}
xplusten commented 2 years ago

Hi @xplusten !

You can do it like so:

bool onPowerState(const String &deviceId, bool &state) {
    if (state) {
        stepper.step(100); // turn on: make 100 steps in positive direction
    } else {
        stepper.step(-100);  // turn off: make 100 steps in negative direction
    }
    return true;
}

Thanks for the response, but I've tried that and it pops up with an error, here is my code. Ive just deleted the old bool onpowerstate and replaced it with yours.

endif

include "SinricPro.h"

include "SinricProSwitch.h"

define WIFI_SSID "YOUR-WIFI-SSID"

define WIFI_PASS "YOUR-WIFI-PASSWORD"

define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx"

define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx"

define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx"

define BAUD_RATE 9600 // Change baudrate to your need

define BUTTON_PIN 0 // GPIO for BUTTON (inverted: LOW = pressed, HIGH = released)

define LED_PIN 2 // GPIO for LED (inverted)

const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution // for your motor

// initialize the stepper library on pins 8 through 11: Stepper myStepper(stepsPerRevolution, D1, D2);

bool myPowerState = false; unsigned long lastBtnPress = 0;

bool onPowerState(const String &deviceId, bool &state) { if (state) { myStepper.step(100); // turn on: make 100 steps in positive direction } else { myStepper.step(-100); // turn off: make 100 steps in negative direction } return true; }

void handleButtonPress() { unsigned long actualMillis = millis(); // get actual millis() and keep it in variable actualMillis if (digitalRead(BUTTON_PIN) == LOW && actualMillis - lastBtnPress > 1000) { // is button pressed (inverted logic! button pressed = LOW) and debounced? if (myPowerState) { // flip myPowerState: if it was true, set it to false, vice versa myPowerState = false; } else { myPowerState = true; } digitalWrite(LED_PIN, myPowerState?LOW:HIGH); // if myPowerState indicates device turned on: turn on led (builtin led uses inverted logic: LOW = LED ON / HIGH = LED OFF)

// get Switch device back
SinricProSwitch& mySwitch = SinricPro[SWITCH_ID];
// send powerstate event
mySwitch.sendPowerStateEvent(myPowerState); // send the new powerState to SinricPro server
Serial.printf("Device %s turned %s (manually via flashbutton)\r\n", mySwitch.getDeviceId().c_str(), myPowerState?"on":"off");

lastBtnPress = actualMillis;  // update last button press variable

} }

// setup function for WiFi connection void setupWiFi() { Serial.printf("\r\n[Wifi]: Connecting"); WiFi.begin(WIFI_SSID, WIFI_PASS);

while (WiFi.status() != WL_CONNECTED) { Serial.printf("."); delay(250); } Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); }

// setup function for SinricPro void setupSinricPro() { // add device to SinricPro SinricProSwitch& mySwitch = SinricPro[SWITCH_ID];

// set callback function to device mySwitch.onPowerState(onPowerState);

// setup SinricPro SinricPro.onConnected([](){ Serial.printf("Connected to SinricPro\r\n"); }); SinricPro.onDisconnected([](){ Serial.printf("Disconnected from SinricPro\r\n"); }); SinricPro.begin(APP_KEY, APP_SECRET); }

// main setup function void setup() { pinMode(BUTTON_PIN, INPUT_PULLUP); // GPIO 0 as input, pulled high pinMode(LED_PIN, OUTPUT); // define LED GPIO as output digitalWrite(LED_PIN, HIGH); // turn off LED on bootup

Serial.begin(BAUD_RATE); Serial.printf("\r\n\r\n"); setupWiFi(); setupSinricPro(); }

void loop() { handleButtonPress(); SinricPro.handle(); }

sivar2311 commented 2 years ago

It would be very nice of you if you can embed lines of code in code blocks. This significantly increases readability. To do this, place ``` one line before and one line after your code.

There is an #endif in the first line of your code. This must be removed! Have you changed your personal access data (AppKey, AppSecret, DeviceId etc)? Which error message do you receive exactly and where does it appear (Serial-Monitor, SinricPro Dashboard)?

xplusten commented 2 years ago

image here is the error. Yes, I filled in all the info, just deleted it, didn't want people hacking me n dat.

Basically, the code is just switch code, where I have replaced the define section with my details and the power boot with the one you have just given me and included the stepper library. And it pops up with the error above.

the switch code is this one btw -https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/examples/Switch/Switch/Switch.ino

sivar2311 commented 2 years ago

In summary: The original example sketch compiles, but as soon as you edit the onPowerState function, you get the error?

xplusten commented 2 years ago

yes

sivar2311 commented 2 years ago

Then something went wrong during editing. Did you notice my advice regarding #endif in the first line of your code?

I recommend to start from the beginning with a fresh example sketch. Then copy and paste the new onPowerState function (see in my comment before) and edit your credentials.

sivar2311 commented 2 years ago

Okay, here is the full code of a compiling sketch.

click to see the sourcecode ```C++ // Uncomment the following line to enable serial debug output //#define ENABLE_DEBUG #ifdef ENABLE_DEBUG #define DEBUG_ESP_PORT Serial #define NODEBUG_WEBSOCKETS #define NDEBUG #endif #include #ifdef ESP8266 #include #endif #ifdef ESP32 #include #endif #include "SinricPro.h" #include "SinricProSwitch.h" #include "Stepper.h" #define WIFI_SSID "YOUR-WIFI-SSID" #define WIFI_PASS "YOUR-WIFI-PASS" #define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" #define BAUD_RATE 9600 // Change baudrate to your need const int stepsPerRevolution = 200; Stepper myStepper(stepsPerRevolution, D1, D2); SinricProSwitch &mySwitch = SinricPro[SWITCH_ID]; // create new switch device bool onPowerState(const String &deviceId, bool &state) { if (state) { // if device is turned on myStepper.step(100); // make 100 steps in positive direction } else { // if device is turned off myStepper.step(-100); // make 100 steps in negative direction } return true; // request handled properly } void setupWiFi() { WiFi.begin(WIFI_SSID, WIFI_PASS); // start wifi Serial.printf("Connecting to WiFi \"%s\"", WIFI_SSID); while (WiFi.status() != WL_CONNECTED) { Serial.printf("."); delay(100); } Serial.printf("connected\r\n"); } void setupSinricPro() { mySwitch.onPowerState(onPowerState); // apply onPowerState callback SinricPro.begin(APP_KEY, APP_SECRET); // start SinricPro } void setupStepper() { // set the speed at 60 rpm: myStepper.setSpeed(60); } void setup() { Serial.begin(BAUD_RATE); setupWiFi(); setupSinricPro(); setupStepper(); } void loop() { SinricPro.handle(); // handle SinricPro commands } ```

Note: There might be an issue about the duration how long the motor is running. For this time, the complete sketch is blocked, but the callback must return within around 6 seconds. For this reason, i made also a non-blocking version:

click to see the sourcecode ```C++ // Uncomment the following line to enable serial debug output //#define ENABLE_DEBUG #ifdef ENABLE_DEBUG #define DEBUG_ESP_PORT Serial #define NODEBUG_WEBSOCKETS #define NDEBUG #endif #include #ifdef ESP8266 #include #endif #ifdef ESP32 #include #endif #include "SinricPro.h" #include "SinricProSwitch.h" #include "Stepper.h" #define WIFI_SSID "YOUR-WIFI-SSID" #define WIFI_PASS "YOUR-WIFI-PASS" #define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" #define BAUD_RATE 9600 // Change baudrate to your need const int stepsPerRevolution = 200; Stepper myStepper(stepsPerRevolution, D1, D2); SinricProSwitch &mySwitch = SinricPro[SWITCH_ID]; // create new switch device int stepsToDo = 0; bool motor_is_running = false; bool onPowerState(const String &deviceId, bool &state) { if (motor_is_running) return true; // if the motor is running, ignore the command if (state) { // if device is turned on stepsToDo = 100; // make 100 steps in positive direction } else { // if device is turned off stepsToDo = -100; // make 100 steps in negative direction } return true; // request handled properly } void handleStepper() { if (stepsToDo == 0) return; // return if nothing is to do... if (stepsToDo > 0) { // if we have to do steps in positive direction myStepper.step(1); // make a single step in positive direction stepsToDo--; // reduce the remaining steps by one } else { // if we have to do steps in negative direction myStepper.step(-1); // make a single step in negative direction stepsToDo++; // reduce the remaining steps (to reduce a negative number we have to add) } if (stepsToDo == 0) motor_is_running = false; // if motion is completed set motor_is_busy to false; } void setupWiFi() { WiFi.begin(WIFI_SSID, WIFI_PASS); // start wifi Serial.printf("Connecting to WiFi \"%s\"", WIFI_SSID); while (WiFi.status() != WL_CONNECTED) { Serial.printf("."); delay(100); } Serial.printf("connected\r\n"); } void setupSinricPro() { mySwitch.onPowerState(onPowerState); // apply onPowerState callback SinricPro.begin(APP_KEY, APP_SECRET); // start SinricPro } void setupStepper() { // set the speed at 60 rpm: myStepper.setSpeed(60); } void setup() { Serial.begin(BAUD_RATE); setupWiFi(); setupSinricPro(); setupStepper(); } void loop() { SinricPro.handle(); // handle SinricPro commands handleStepper(); // handle Stepper motor } ```
xplusten commented 2 years ago

YES! thank you very much, for some reason my credentials were wrong causing that error, appreciate your help very much

xplusten commented 2 years ago

Hey @sivar2311 just wondering with the non blocking version code, could you please explain what you did? or send a link where I can learn how you did that? Thanks, xplusten.

sivar2311 commented 2 years ago

I thought I had already written a sufficient description in the comment lines.

Roughly explained:

The concept is actually the same as BlinkWithoutDelay, with the difference that here not time but the steps of the stepper motor are used. (Btw: The same concept is used by the SinricPro.handle() function).

Explained in detail: The onPowerState function no longer does the (blocking) work itself (myStepper.step(100)), but stores the information how many steps should be done (stepsToDo = 100). This makes the onPowerState function very fast.

The blocking part is contained in the function handleStepper(). There it is checked if the variable stepsToDo is 0. If this is the case, the function is terminated (because there is nothing to do). Otherwise, only a single step is done (this does not take as much time as doing all steps at once). Then the variable stepsToDo is reduced by one (or increased by one if the steps are to be done in the negative direction).

The function handleStepper() is called each time the loop() function is run. This gives the rest of the sketch enough time for other work.

The link you asked for: BlinkWithoutDelay

stale[bot] commented 2 years ago

This issue has gone quiet. Spooky quiet. We currently close issues after 14 days of inactivity. It’s been at least 7 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. As a friendly reminder, the best way to fix this or any other problem is to provide a detailed error description including a serial log. Thanks for being a part of the SinricPro community!

stale[bot] commented 2 years ago

Hey again! It’s been 14 days since anything happened on this issue, so our friendly robot (that’s me!) is going to close it. Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to comment on this issue or create a new one if you need anything else. As a friendly reminder, the best way to fix this or any other problem is to provide a detailed error description including a serial log. Thanks again for being a part of the SinricPro community!

gudmund1 commented 9 months ago

Okay, here is the full code of a compiling sketch.

click to see the sourcecode Note: There might be an issue about the duration how long the motor is running. For this time, the complete sketch is blocked, but the callback must return within around 6 seconds. For this reason, i made also a non-blocking version:

click to see the sourcecode

Hi, thank you for sharing this sketch. I'm using it for a rail mounted curtain operating device and it works like a charm! The only problem I'm having is that the stepper motors get really hot when idle, so I'm trying to figure out is how to control the ENABLE pin on the A4988 driver. The ENABLE pin is low by default, and needs to be pulled high to cut power to the motor. How would I include this in the sketch? I tried adding this to the loop at the bottom of the sketch but somehow it didn't work... Thanks for any advice!

sivar2311 commented 9 months ago

Hi @gudmund1 For your application, I would recommend using the Blinds device. See the example Blinds.

It supports the onPowerCallback as well as the onRangeValue callback.

The onRangeValue callback gives you a value between 0 and 100 which you only have to convert into the required number of steps for the stepper motor.

The Blinds device then also lets Alexa process statements like:

Alexa, open the bedroom blinds.
Alexa, close the bedroom blinds.

You can then use the onPowerState callback to control the enable pin.

Please note that this issue has been closed for two years. For further questions, please open a new issue.