kabelbaan tussenstation met arduino

Plaats reactie
Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

kabelbaan tussenstation met arduino

Bericht door fenix »

goede dag,

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:
/*
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();
}
voor suggesties om de code aan te passen, hou ik me aanbevolen. Ik ben het schrijven van code nog aan het leren ;)
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



Onmogelijkhier
Berichten: 410
Lid geworden op: 05 jan 2021 20:06
Locatie: Omgeving Rotterdam mijn geboorte stad

Re: kabelbaan tussenstation met arduino

Bericht door Onmogelijkhier »

Dat is weer een flinke hoeveelheid code Fenna.


Groeten Marcel V
Gebruikersavatar

shoven
Berichten: 6103
Lid geworden op: 14 dec 2008 21:06
Locatie: Walcheren

Re: kabelbaan tussenstation met arduino

Bericht door shoven »

Lekker bezig hoor.

Je vroeg om tips. Op de code an sich heb ik niet zoveel aan te merken. Wat wel opvalt is het gebruik van globale variabelen, dat zou ik zoveel mogelijk vermijden; zeker bij wat grotere programma's verlies je al snel het overzicht wat waar wordt gemanipuleerd. Idealiter heb je er nul, maar de opbouw van een Arduino-programma met zijn setup- en loop-deel maakt dat nagenoeg onmogelijk. Het gebruiken van parameters bij aanroepen van subroutines helpt.

De andere tip is het verbeteren van de leesbaarheid en daarmee de begrijpbaarheid. Het is nu nogal een letterbrij (komt ook doordat de editor van het forum k* omgaat met tabs). Dat is te verbeteren door de code wat ruimer op te schrijven.

Concreet:
* if, while etc: Zet de if met de conditie op de ene regel en de code die moet worden uitgevoerd op de volgende.
* Zet uit te voeren code altijd tussen accolades die op een eigen regel staan. Ook als er maar 1 regel code uit te voeren is.
* Royaal gebruiken van spaties: while( conditie ) leest makkelijker dan while(conditie), omdat het rustiger is voor het oog.
* Ingewikkelde berekeningen kun je over meerdere statements spreiden waar nodig.
* Royaal commentaar toevoegen, niet alleen bij de kop van een subroutine maar ook tussendoor.

Zomaar wat dingetjes van een voormalig C/C++ programmeur. Ongetwijfeld zijn er die dit niks vinden, maar ik vond het altijd prettig werken. De compiler maakt het niet uit, maar het is altijd fijn als een ander (of jijzelf een half jaar later ;) ) de code begrijpt. Een enter, spatie of accolade extra typen kost je de kop niet tenslotte.


Steven

Hier is mijn baan op het forum
Hier is mijn vorige baan op het forum
Hier is mijn voor-vorige baan op het forum
>>> Filmpjes van de baan
Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

Hallo Steven,
Dank voor je feedback.

De inspringing van de code wordt door de forum editor volledig verwijderd. In het broncode bestand is dat wel voor elkaar.
De accolades bij if/else/while routines ed is een discipline die ik inderdaad wat strakker kan aanhouden bij enkele statements.

Het gebruik van globale variabelen is ook een dingetje en heeft ook zijn gevolgen gehad die ik had op te lossen. De changelog van versie 0.4 naar 0.5 gaat daar over.
Evengoed heb ik ze hier bewust gebruikt, bij gebrek aan kennis over een mogelijk elegantere oplossing, om de 'status' van wat er aan de hand is beschikbaar te hebben voor de loop. Mijn implementatie van het debug display en de statusled hangt er ook van af.
ook wordt op basis van een globale boolean waarde in een functie iets wel of niet gedaan, of op een wat andere manier.

Waar ik aan denk, om het toch iets eleganter te krijgen, is de boolean variabelen die in een routine worden gebruikt in de aanroep mee te geven, zodat ze binnen de routine lokaal worden verwerkt en teruggegeven.

hmm dit schrijvende kom er iets met een struct naar boven.
Zou dat de elegantere oplossing zijn? in ieder geval een leuke aanleiding om er wat mee te gaan spelen ;)
Het gebruik van een struct is nog nieuw voor me en ik zal vast wel een paar keer op mijn neus gaan ;) zonder te vallen leer je niet opstaan 8-)

Fenna
edit: de forum editor sloopt ook alle spaties eruit die voor leesbaarheid wel in de broncode staan


Gebruikersavatar

Momfer
Berichten: 570
Lid geworden op: 29 okt 2020 13:13
Locatie: Zwolle

Re: kabelbaan tussenstation met arduino

Bericht door Momfer »

Als je de Afbeelding-knop gebruikt voor code i.p.v. de Afbeelding-knop, dan worden de extra spaties en/of tabs beter weergegegeven.

Niet ideaal, maar beter als niks zullen we maar zeggen :) en als extraatje: selecteer code :)

Voorbeeldje:

eerste deel met TABS (springt best ver in)
tweede deel met 2, 4 en 6 spaties voor inspringen

Code: Selecteer alles

//eerste deel met <TABS>
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);

//tweede deel, alleen spaties
  if ((interrupt_time - last_interrupt_time > _DEBOUNCE_TIME2)&& stopPinCheck) // 2 spaties
    { // 4 spaties
      _STOP=true; // 6 spaties
      _WAIT=false;
      _START=false;
    }
  last_interrupt_time = interrupt_time;
}


Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

Hey Dank :)

met die tag ziet het er iets beter uit ;)

Code: Selecteer alles

/*
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();
}
plus de post als geheel wordt leesbaarder


Gebruikersavatar

Momfer
Berichten: 570
Lid geworden op: 29 okt 2020 13:13
Locatie: Zwolle

Re: kabelbaan tussenstation met arduino

Bericht door Momfer »

En zo worden we allemaal weer wijzer van elkaar :)

_o_ _o_ _o_


Gebruikersavatar

shoven
Berichten: 6103
Lid geworden op: 14 dec 2008 21:06
Locatie: Walcheren

Re: kabelbaan tussenstation met arduino

Bericht door shoven »

Dat scheelt zeker.

Een struct is zeker een goed idee Fenna, maar kent ook zijn eigen uitdagingen. Je kunt ook nog met pointers werken, dat geeft wat meer mogelijkheden (en bijpassende gevaren :mrgreen: ).
Maar vooral lekker proberen en kijken wat voor jou werkt.


Steven

Hier is mijn baan op het forum
Hier is mijn vorige baan op het forum
Hier is mijn voor-vorige baan op het forum
>>> Filmpjes van de baan
Gebruikersavatar

PaulRoman
Berichten: 154
Lid geworden op: 25 feb 2017 13:48
Locatie: RO

Re: kabelbaan tussenstation met arduino

Bericht door PaulRoman »

Hallo Fenna,

Gelukkg heb je de "CODE" knop op het forum ontdekt. Dat maakt je programmatekst in ieder geval al een stuk beter leesbaar.
Met alle respect voor de effort die je al in de de ontwikkeling hebt gestoken, heb ik wel wat aan- en opmerkingen bij je code.
Je gebruikt veel #define, die zou ik vervangen door const's. Dan heeft de compiler er meer controle over. Ik zie veel verschillende vormen van naamgeving van variabelen en functions. Gebruik een consistente wijze van naamgeven. Veel gebruikt is de lowerCamelCase voor variabelen en functies, en de UpperCamelCase voor classes. De underscore (_) die je in sommige variabelenamen gebruikt wordt over het algemeen gebruikt voor protected en private variabelen in classes. Een erg nuttig boekwerkje vind ik persoonlijk "The Elements of C++ Style" door Trevor Misfeldt, Gregory Bumgardner en Andrew Gray, uitgegeven by Cambridge University Press . Je kunt natuurlijk je eigen ideeen hebben over sommige zaken, maar wees wel consequent. Zo gebruik ik zelf geen _variabelnaam in classes, maar m_variabelenaam, omdat ik vind dat ik snel over de underscore heen lees. Ik geloof dat dat "m_" ergens uit de Microsofthoek kwam, maar ik vind het veel leesbaarder.
Het belangrijkste echter: als ik jou was zou ik me snel verdiepen in het schrijven en gebruiken van classes. Daarmee concentreer je de functionaliteit in de classes die je ontwikkelt, en wordt je hoofdprogramma eigenlijk alleen een soort controle structuur. Wijzigingen in functionaliteit maak je dan in de classes. Het beinvloedt je programmastructuur nauwelijks, en je kunt uiteraard ook tests schrijven voor elke class afzonderlijk, zodat je weet dat dat onderdeel naar behoren werkt. Nu vind ik je programma slecht te begrijpen, omdat alles door elkaar heen staat. Denk ook eens aan state-transition diagrammen om je flow vast te leggen.
Zie bijvoorbeeld mijn post over de IRSensor. Naamgeving overigens afwijkend, want eigenlijk zou dit IrSensor moeten heten. Door het gebruik van "new" en het ontbreken van de corresponderende "delete" zit er wel een memoryleak in, maar door de structuur van een Arduino-programma met setup() en loop() ontkom je er m.i. niet aan, want de delete's zouden in de main moeten staan, en die voegt de Arduino IDE zelf toe.
Zie ook veel (maar lang niet alle) libraries die je op internet vindt voor goede voorbeelden van het programmeren van classes.
Begrijp me goed, Fenna, ik kraak je niet af, maar hoop dat je er beter van wordt. Vooral als je veel software schrijft, resp. wil gaan schrijven.

Wil je de specificaties van je huidige project hier nog eens posten ?

Succes in ieder geval met je project. De Geinbaan zal er blij mee zijn.

Paul


Schäßburg an der Kokel
Gebruikersavatar

PaulRoman
Berichten: 154
Lid geworden op: 25 feb 2017 13:48
Locatie: RO

Re: kabelbaan tussenstation met arduino

Bericht door PaulRoman »

Hoi Fenna,

Als ik je sketch nog even snel bekijk stel je de vertraging en versnelling van de gondel in met een potmeter. Maar houd er rekening mee dat als de melder die de gondel triggert om te gaan stoppen op een vaste afstand ligt voor het gewenste stoppunt, dat dan de (negatieve) versnelling die nodig is om precies daar te stoppen al vastligt. Bij een gegeven afstand "d" tussen melder en stoppunt en een gegeven snelheid "v" bij het passeren van de melder is deze versnelling a=-v^2/2d. Een negatieve versnelling, dus een vertraging. De tijd die nodig is om het stoppunt te bereiken t=2*d/v. Voor het optrekken na de stop kun je uiteraard wel de waarde van de potmeter gebruiken.

Groet,

Paul


Schäßburg an der Kokel
Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

Hallo Paul,

Op je reactie van vanmorgen kom ik dadelijk apart terug.

Je reactie van vanmiddag is ook (in zekere zin) terecht. De sensor komt uiteindelijk op een vaste positie en ook de basissnelheid komt vast te liggen. Beide zijn nu echter volkomen onbekend. ook Janiegein, de uiteindelijke gebruiker, kan daar waarschijnlijk nu nog geen duidelijk antwoord op geven. Het enige wat ik nu wel weet is de rpm van de aandrijfmotor. Met welke overbrenging dat terecht komt op de kabelbaan??? geen idee nog. Afstand en snelheid beide dus onbekend.

Dan kan ik wel alvast stoer proberen een afremtijd uit te rekenen, maar dat is wat zonde van mijn tijd. Als ik het voor mezelf zou bouwen, zou ik dat zomaar wel op die manier kunnen gaan uitwerken en dan de potmeter gebruiken voor wat fine-tuning. Jan werkt op een wat andere manier. Bovendien is de kans erg groot dat Jan het in gebruik neemt als ik er niet bij ben om in de code wat parameters bij te stellen en de arduino opnieuw te programeren

Daarom voor de geinbaan een wat meer generieke oplossing met een potmeter om een totale afremtijd in te stellen. Wel kan ik nog wat stoeien met het afrem profiel. Dat is nu lineair, maar dat wordt nog wel een s curve (bij benadering) voor een wat realistischer verloop.

gr Fenna


Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

PaulRoman schreef: 11 apr 2022 08:52 Hallo Fenna,

Gelukkg heb je de "CODE" knop op het forum ontdekt. Dat maakt je programmatekst in ieder geval al een stuk beter leesbaar.
Met alle respect voor de effort die je al in de de ontwikkeling hebt gestoken, heb ik wel wat aan- en opmerkingen bij je code.
Je gebruikt veel #define, die zou ik vervangen door const's. Dan heeft de compiler er meer controle over. Ik zie veel verschillende vormen van naamgeving van variabelen en functions. Gebruik een consistente wijze van naamgeven. Veel gebruikt is de lowerCamelCase voor variabelen en functies, en de UpperCamelCase voor classes. De underscore (_) die je in sommige variabelenamen gebruikt wordt over het algemeen gebruikt voor protected en private variabelen in classes. Een erg nuttig boekwerkje vind ik persoonlijk "The Elements of C++ Style" door Trevor Misfeldt, Gregory Bumgardner en Andrew Gray, uitgegeven by Cambridge University Press . Je kunt natuurlijk je eigen ideeen hebben over sommige zaken, maar wees wel consequent. Zo gebruik ik zelf geen _variabelnaam in classes, maar m_variabelenaam, omdat ik vind dat ik snel over de underscore heen lees. Ik geloof dat dat "m_" ergens uit de Microsofthoek kwam, maar ik vind het veel leesbaarder.
Het belangrijkste echter: als ik jou was zou ik me snel verdiepen in het schrijven en gebruiken van classes. Daarmee concentreer je de functionaliteit in de classes die je ontwikkelt, en wordt je hoofdprogramma eigenlijk alleen een soort controle structuur. Wijzigingen in functionaliteit maak je dan in de classes. Het beinvloedt je programmastructuur nauwelijks, en je kunt uiteraard ook tests schrijven voor elke class afzonderlijk, zodat je weet dat dat onderdeel naar behoren werkt. Nu vind ik je programma slecht te begrijpen, omdat alles door elkaar heen staat. Denk ook eens aan state-transition diagrammen om je flow vast te leggen.
Zie bijvoorbeeld mijn post over de IRSensor. Naamgeving overigens afwijkend, want eigenlijk zou dit IrSensor moeten heten. Door het gebruik van "new" en het ontbreken van de corresponderende "delete" zit er wel een memoryleak in, maar door de structuur van een Arduino-programma met setup() en loop() ontkom je er m.i. niet aan, want de delete's zouden in de main moeten staan, en die voegt de Arduino IDE zelf toe.
Zie ook veel (maar lang niet alle) libraries die je op internet vindt voor goede voorbeelden van het programmeren van classes.
Begrijp me goed, Fenna, ik kraak je niet af, maar hoop dat je er beter van wordt. Vooral als je veel software schrijft, resp. wil gaan schrijven.

Wil je de specificaties van je huidige project hier nog eens posten ?

Succes in ieder geval met je project. De Geinbaan zal er blij mee zijn.

Paul
Hallo Paul,

Met je opbouwende kritiek ben ik blij. Met het schrijven van code ben ik nog maar een beginnend amateurtje, dus je helpt me enorm met je reactie :)
Hoe snel (en in hoeverre) ik het uitrol in mijn code zal gaan blijken ;)
Het belangtrijkste wat ik hier lees is om een consequente stijl te hanteren. En het helpt als die stijl bestaande conventies volgt.

Over het verschil tussen defines en consts ga ik me nog wat meer inlezen om de voor- en nadelen van elk beter te begrijpen.

In ieder geval komt dat boek op mijn wensenlijstje. En op mijn oefen lijstje komen nu structs en classes te staan ;)

Voor dit kabelbaan project ben ik op pad gestuurd met: "ik wil een kabelbaan gondel bij een tussenstation laten stoppen. Kun je me helpen?" (een beetje geparafraseerd, maar wel die strekking) Ik weet van de motor de spanning, stroom en rpm. Verder is er wat gebrainstormt over de positie en soort/type van de sensor en dat een harde stop en start onwenselijk zijn om de preisertjes gezond te houden. Verder is het voor mij: "enjoy!" ;)
Waar de gondel uithangt als de baan wordt opgestart, is waar de gondel op dat moment toevallig is. Het kan zijn dat de gondel binnen de opstart versnelling de sensor passeert om af te remmen. Dat gaf in versie 0.4 wat complicaties ;)
Tijdens het ontwikkelen van code heb ik graag een display op de arduino om wat debug info op te tonen.Vandaar de u8g2 library. Dat ga ik misschien nog omwerken naar de u8x8 library, omdat die wat zuiniger is met RAM (geen buffers) en ik toch alleen maar tekst op het scherm zet.

Voor mezelf is het ook een mooie aanleiding om mijn eigen rijregelaar wat extra functionaliteit te geven.

Speciefiek voor dit kabelbaan project denk ik dat de code alleen nog een ander afrem/versnel profiel krijgt (s curve) en dat ik spelen met structs en classes bewaar voor de verdere ontwikkeling van mijn rijregelaar (die ook een I2C verbinding heeft met een RPi voor evt loc specifieke snelheid, afrem en versnel eigenschappen (misschien zelfs een loc specifieke PWM frequentie) en een override voor automatisch rijden)

gr. Fenna


Gebruikersavatar

PaulRoman
Berichten: 154
Lid geworden op: 25 feb 2017 13:48
Locatie: RO

Re: kabelbaan tussenstation met arduino

Bericht door PaulRoman »

Hoi Fenna,

Nog even voor de volledigheid: dat boekje "The Elements of C++ Style" gaat puur over de stijl van programmeren. Er staat totaal niets in waarmee je C++ zou kunnen leren. Het gaat uit van een gedegen kennis van C++. Voor het leren van C++ is er uiteraard veel literatuur beschikbaar.
Nog even over je programma. Waarom ben je tot het gebruik van een interrupt gekomen om de pushbutton te implementeren ? Zo tijd-kritisch is dat toch niet ? Bovendien gebeurt er erg veel in de interrupt service routine, en zie ik nergens een volatile gebruikt. Persoonlijk zou ik een sensortje (class !) schrijven dat simpelweg de contactdender afvangt, en als output zoiets als hasRisen() en hasFallen() kan afgeven. Zo heb ik het althans zelf ooit gedaan.

Groet,

Paul


Schäßburg an der Kokel
Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

hallo Paul,

Dat is me helder.
Voor het leren van C++ heb ik al een aantal boeken :)
Het zijn Nederlands talige boeken en tja ... voor dit soort zaken is het handiger om een engels talig boek te hebben .. hoeft er geen jargon vertaald te worden naar iets dat er wel een beetje op lijkt, maar waar je net niet mee opschiet in een internationaal speelveld ;) :D 8)7
(denkend aan "The standard Marine Vocabulary" voor de zeevaart waarin bepaalde woorden en zinnen een strak gedefinieerde betekenis hebben om de zeescheepvaart veilig te houden.)

gr. Fenna


Gebruikersavatar

janiegein
Berichten: 3554
Lid geworden op: 26 okt 2012 12:33
Locatie: Nieuwegein sinds 05*12*23 Utrecht

Re: kabelbaan tussenstation met arduino

Bericht door janiegein »

Fenna,

met diep respect en bewondering heb ik bovenstaande artikelen door gebladerd en mij absoluut niet gerealiseerd dat wat ik aan jou gevraagd heb allemaal teweeg gebracht heeft en benodigd lijkt te hebben om dat te realiseren.

zoals gezegd met mijn lekenverstand snap ik daar helemaal niets van en begrijp ook dat er meerdere onder ons zijn die dit soort taal als "normale "correspondentie zien.

ik denk alleen dat wat jij/ jullie bedenken cq opschrijven veel te moeilijk gedacht wordt.

als er in het programma ergens de motor van vol vermogen naar nul gedraaid wordt vervolgens vijf seconden wacht en dan weer langzaam naar vol vermogen gaat heeft die een bepaalde lengte nodig op de kabelbaan die dan het uiteindelijk stoppunt bepaald.

als ik dat wonder apparaat waar nu zoveel over geschreven wordt zou hebben en dan een proefopstelling zou maken dan kan ik toch berekenen hoeveel lengte er nodig is om stopplaats te bereiken en aan de hand van het moment dat ding simpel stopopdracht binnen krijgen en vandaaruit dit gegeven ( lengte wat nodig is om te stoppen ) het stopstation op de baan te bepalen en maken?

of denk ik mu te simpel. snap dat dat ook geprogrammeerd moet worden maar toch.

nogmaals diep respect en ja kan niet anders dan afwachten wanneer er voor jou een bevredigend eindresultaat ontstaat.


gr jan

bouwer van de Geinbaan ( analoog )
we moeten roeien met de riemen die we hebben en wachten op betere tijden, voor nu zijn dat helaas wat kleine peddeltjes geworden.
Gebruikersavatar

Onderwerp starter
fenix
Berichten: 595
Lid geworden op: 04 apr 2019 14:26

Re: kabelbaan tussenstation met arduino

Bericht door fenix »

Hallo Jan,

In leken taal:
Als je straks je kabelbaan ophangt en het tussenstation bouwt, zet je het kastje aan om de kabelbaan te laten draaien. Ergens heb je het reedcontact zitten. De gondel komt langs de sensor, remt af en stopt. Stopt de gondel te vroeg, draai je de potmeter de ene kant op, stopt de gondel te laat, draai je de potmeter de andere kant op. Moet de gondel te snel stoppen en krijgen de preisertjes hoofdpijn van tegen het glas vliegen, doe je de sensor wat verder weg van het tussenstation. Kunnen ze nog een bakkie doen voordat de gondel stil staat, doe je de sensor wat dichterbij het tussenstation.

De andere potmeter bepaalt hoe lang de gondel blijft wachten. Die regel ik vooraf op ongeveer 5 seconden.

Over die technische correspondentie: dat is een beetje sparren over welke route naar Rome kan worden genomen. Dat is voor mij ook deel van de hobby ;)

groetjes, Fenna


Plaats reactie