Voor de geinbaan ben ik bezig om met een arduino een besturing te maken die een kabelbaan bij een tussenstation laat stoppen.
De code heb ik voorzien van GPLv3 (ofwel, met een paar kanttekeningen mag je er naar eigen inzicht in aanpassen wat je wilt
Voor de licentie tekst: GPLv3 in text
de code:
voor suggesties om de code aan te passen, hou ik me aanbevolen. Ik ben het schrijven van code nog aan het leren/*
Cable car control for Geinbaan, version 0.5
Copyright (C) 2022 Fenna Pel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
changelog:
0.1: initial version to get the main features going.
- detecting a stop sensor, slow down to 0 in a controlled manner, wait a certain time and then a gradual speed up to normal speed
0.2:
- include a status led (WS2812b):
- green, full brightness: normal operating speed
- green, increasing brightness: speeding up to nomal speed
- orange, decreasing brightness: speed is slowing down
- red, decreasing brightness: waiting time is passing
- added a hardware flag (pin 7) to use or not use an I2C connected oled (128x64)
0.3:
- reworked the PWM frequency setting to use dipswitches for timer2 prescaling (pin 8,9 and 10)
0.4:
- added licence information and copyright notice
0.5:
- fixed the issue that when a new stop interrupt was triggered while still dealing with a
previous one (or during first spin-up), some unexpected behaviour occured
- removed commented-out code lines
- renamed project to "kabelbaan-geinbaan"
*/
#include <Arduino.h>
#include <U8g2lib.h> // uncomment this line to use u8g2
#include <Wire.h> // uncomment this line to use u8g2
#include <SPI.h> // uncomment this line to use u8g2
#include <FastLED.h>
// L239D en L298N motordrivers gebruiken dezelfde signalen van de arduino
#define enablePin 11 // enable pin van een L239D motorregelaar
#define in1Pin 4 // inputpin 1 van een L239D motorregelaar
#define in2Pin 5 // inputpin 2 van een L239D
#define stoptriggerPin 2 // detectie schakelaar voor stop en wacht functie (de drukknop verbindt de pin met massa)
#define _DEBOUNCE_TIME2 300 // deze waarde wordt gebruikt om 'dender' in de wacht trigger af te vangen. tweak this number
#define _WAIT_TIME_SET_PIN A0
#define _DECELERATE_TIME_PIN A1
#define _MAX_DESC_TIME 5000
#define _MAX_WAIT_TIME 10000
#define _MAX_STEP_COUNT 20
#define _MAX_SPEED 255
#define _USE_OLED_PIN 7
#define _PRESCALE_0 8
#define _PRESCALE_1 9
#define _PRESCALE_2 10
#define DATA_PIN 6
#define NUM_LEDS 1
#define _BRIGHT_FACTOR 6
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
int speedStep;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
boolean _USE_OLED;
boolean _WAIT = false; // global flag
boolean _STOP = false; // global flag
boolean _START = false; // global flag
boolean _ACCELERATE = false;
boolean _DECELERATE = false;
boolean _WAIT_CYCLE = false;
int _SPEED;
int stepCounter;
int accelerateValue;
unsigned long stepTime;
unsigned long laststepTime;
unsigned long endwaitTime;
unsigned long waitTimeLeft;
unsigned long waitTime;
unsigned int waitBright;
unsigned int ledBright;
byte prescaleFactor = 0;
void stopint()
{
// no interfacing to hardware here. just some flag setting to let the main loop take care of the hardware handling
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
boolean stopPinCheck;
stopPinCheck = digitalRead(!stoptriggerPin);
if ((interrupt_time - last_interrupt_time > _DEBOUNCE_TIME2)&& stopPinCheck)
{
_STOP=true;
_WAIT=false;
_START=false;
}
last_interrupt_time = interrupt_time;
}
void printdisplay()
{
u8g2.clearBuffer();
u8g2.setCursor(0,10);
u8g2.print("Stop: ");
u8g2.println(_STOP);
u8g2.print(" Desc: ");
u8g2.println(_DECELERATE);
u8g2.setCursor(0,20);
u8g2.print("Wait: ");
u8g2.println(_WAIT);
u8g2.print(" Wcyc: ");
u8g2.println(_WAIT_CYCLE);
u8g2.setCursor(0,30);
u8g2.print("Start: ");
u8g2.println(_START);
u8g2.print(" Acc: ");
u8g2.println(_ACCELERATE);
u8g2.setCursor(0,40);
u8g2.print("Speed: ");
u8g2.println(_SPEED);
u8g2.setCursor(0,50);
u8g2.print("Step: ");
u8g2.println(stepCounter);
u8g2.print(" stpTm: ");
u8g2.println(stepTime);
u8g2.setCursor(0,60);
u8g2.print("waitTL: ");
u8g2.println(waitTimeLeft);
u8g2.sendBuffer();
}
void setup()
{
// Setting up the prescaler for timer 2 (pin 11 and 3)
pinMode(_PRESCALE_0, INPUT_PULLUP);
pinMode(_PRESCALE_1, INPUT_PULLUP);
pinMode(_PRESCALE_2, INPUT_PULLUP);
if (digitalRead(_PRESCALE_0)) bitSet(prescaleFactor,0);
if (digitalRead(_PRESCALE_1)) bitSet(prescaleFactor,1);
if (digitalRead(_PRESCALE_2)) bitSet(prescaleFactor,2);
TCCR2B = (TCCR2B & B11111000) | prescaleFactor; // set timer 2 prescaler according to the dipswitches
/*
prescaleFactor == B00000001 : for PWM frequency of 31372.55 Hz
prescaleFactor == B00000010: for PWM frequency of 3921.16 Hz
prescaleFactor == B00000011: for PWM frequency of 980.39 Hz
prescaleFactor == B00000100: for PWM frequency of 490.20 Hz
prescaleFactor == B00000101: for PWM frequency of 245.10 Hz
prescaleFactor == B00000110: for PWM frequency of 122.55 Hz
prescaleFactor == B00000111: for PWM frequency of 30.64 Hz
*/
//setup pin 11 AND 3 for phase correct PWM:
TCCR2A = _BV(COM2A1) | _BV(WGM20);
TCCR1A = _BV(COM1A1) | _BV(WGM20);
pinMode(in1Pin, OUTPUT);
pinMode(in2Pin, OUTPUT);
pinMode(enablePin, OUTPUT);
pinMode(_WAIT_TIME_SET_PIN, INPUT);
pinMode(_DECELERATE_TIME_PIN, INPUT);
pinMode(stoptriggerPin, INPUT_PULLUP);
pinMode(_USE_OLED_PIN, INPUT_PULLUP);
_USE_OLED=!digitalRead(_USE_OLED_PIN);
attachInterrupt(digitalPinToInterrupt(stoptriggerPin), stopint, FALLING); // de interrupt routines handelen ook
//'contact dender' af en geven aan het hoofdprogramma door in welke stand de wissels moeten staan
// daarvoor gebruiken de interrupt routines 2 software 'vlaggen': turnleft en turnright
// setup initiates start flag and speed for smooth start. The mainloop does the actual work:
_SPEED = 0;
_START = true;
speedStep = _MAX_SPEED / _MAX_STEP_COUNT;
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
if(_USE_OLED) printdisplay();
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
}
void stop_routine()
{
unsigned long accelerateTime;
unsigned long currentTime;
if (_STOP)
{
// only the stop flag set, the descelerate flag not yet. only run at first pass after the sensor interrupt triggered
// the decelerate flag is used as a marker to keep stepping down the speed while the speed is > 0
accelerateValue = analogRead(_DECELERATE_TIME_PIN);
accelerateTime = map(accelerateValue,0,1023,0,_MAX_DESC_TIME);
laststepTime = millis();
_DECELERATE = true;
_STOP = false;
if (!_ACCELERATE) stepCounter = _MAX_STEP_COUNT;
stepTime = accelerateTime / _MAX_STEP_COUNT;
if (_WAIT || _WAIT_CYCLE)
{
speedStep = 0;
stepCounter = 0;
}
if (_ACCELERATE) speedStep = _SPEED / stepCounter;
}
if (_DECELERATE)
{
currentTime = millis();
if ((currentTime - laststepTime) >= stepTime)
{
stepCounter = stepCounter - 1;
laststepTime = currentTime;
if (stepCounter > 0 )
{
_SPEED = _SPEED - speedStep;
if (_SPEED < 0) _SPEED = 0;
}
else
{
// reached target stopping time, set speed to 0 and unset stop flag and decelerate flag to avoid running this function again
_SPEED = 0;
_DECELERATE = false;
_STOP = false;
_WAIT = true;
}
}
}
}
void wait_routine()
{
// monitors the wait time until the set time to wait has passed, then unset the wait flag and set the start flag
// after the set waiting time, the wait flag is unset and the start flag is set
unsigned long currentTime;
int endwaitValue;
if (_WAIT && !_WAIT_CYCLE)
{
speedStep = _MAX_SPEED / _MAX_STEP_COUNT;
_WAIT_CYCLE = true;
endwaitValue = analogRead(_WAIT_TIME_SET_PIN);
endwaitTime = map(endwaitValue,0,1023,0,_MAX_WAIT_TIME);
waitTime = endwaitTime+millis();
_WAIT = false;
}
if (_DECELERATE) _WAIT_CYCLE = false;
currentTime = millis();
waitTimeLeft = waitTime - currentTime;
if (currentTime > waitTime)
{
_WAIT_CYCLE = false;
_START = true;
waitTimeLeft = 0;
}
}
void start_routine()
{
unsigned long accelerateTime;
unsigned long currentTime;
if (_START)
{
// only the stop flag set, the descelerate flag not yet. only run at first pass after the sensor interrupt triggered
// the decelerate flag is used as a marker to keep stepping down the speed while the speed is > 0
accelerateValue = analogRead(_DECELERATE_TIME_PIN);
accelerateTime = map(accelerateValue,0,1023,0,_MAX_DESC_TIME);
laststepTime = millis();
_ACCELERATE = true;
_START = false;
stepCounter = _MAX_STEP_COUNT;
stepTime = accelerateTime / _MAX_STEP_COUNT;
_SPEED = 0;
}
if (_ACCELERATE)
if (_DECELERATE) _ACCELERATE= false;
{
currentTime = millis();
if ((currentTime - laststepTime) >= stepTime)
{
stepCounter = stepCounter - 1;
laststepTime = currentTime;
if (stepCounter > 0 ) _SPEED = _SPEED + speedStep;
else
{
// reached target stopping time, set speed to 0 and unset stop flag and decelerate flag to avoid running this function again
_SPEED = _MAX_SPEED;
_ACCELERATE = false;
_START = false;
}
}
}
}
void loop()
{
// these functions manipulate the speed, based on software flags:
if (_STOP || _DECELERATE) stop_routine();
if (_WAIT || _WAIT_CYCLE) wait_routine();
if (_START || _ACCELERATE) start_routine();
// output actual speed to the motor controller.
analogWrite(enablePin, _SPEED);
ledBright = _SPEED / _BRIGHT_FACTOR;
waitBright=map(waitTimeLeft,0,endwaitTime,0,255/_BRIGHT_FACTOR);
leds[0].r=(_DECELERATE*ledBright/1.2)+(_WAIT_CYCLE*waitBright/1.2);
leds[0].g=(_ACCELERATE*ledBright/1.2)+(_DECELERATE*ledBright/1.7);
if (!_START && !_ACCELERATE && !_STOP && !_DECELERATE && !_WAIT && !_WAIT_CYCLE) leds[0].g=ledBright/1.2;
leds[0].b=(_DECELERATE*ledBright/20);
FastLED.show();
// if a display is connected, display debug info
if (_USE_OLED) printdisplay();
}
Ook als je een bug vindt, hoor ik het graag.
Een aantal zaken kunnen nog wat netter, zoals bijvoorbeeld de waardes van variabelen meegeven naar de functies. Daar wordt nog aan gewerkt
De hardware is inmiddels klaar om in een kastje in te bouwen.
Nu is het eerst bedtijd. Straks overdag heb ik wel tijd voor een filmpje en wat plaatjes
Fenna