2020-04-01 11:01:07 +00:00
/*
* Ideas / TODO :
* POT_MIN , POT_MAX as variable with calibration procedure . Drive slowly to both ends until value does not get lower .
* Motor error checking . Timeout overall ( if regulation fails or stuck ) . Timeout movement ( motor is tunring but no change in poti value detected ) . Move right direction .
2020-04-12 10:34:14 +00:00
* Hardware : motorentstörkondensatoren einbauen direkt an motor . 47 nF + zu - und zwei 10 nF + zu case und - zu case
* PI Optimieren . aktuell overshoot
* Implement knob menu structure
2020-04-01 11:01:07 +00:00
*/
2020-03-31 15:13:21 +00:00
# include <Arduino.h>
# include <Adafruit_NeoPixel.h>
# ifdef __AVR__
# include <avr/power.h>
# endif
2020-04-01 09:37:09 +00:00
void reconnect ( ) ;
uint32_t Wheel ( byte WheelPos ) ;
boolean srRead ( uint8_t pbit ) ;
void srWrite ( uint8_t pbit , boolean state ) ;
void callback ( char * topic , byte * payload , unsigned int length ) ;
2020-04-03 13:38:59 +00:00
void srShiftOut ( ) ;
void setMuteInt ( uint8_t i ) ;
void setSelectionInt ( uint8_t i ) ;
boolean getSelection ( uint8_t pbit ) ;
boolean getMute ( uint8_t pbit ) ;
void setSelectionChannel ( uint8_t i , boolean state ) ;
void setMuteChannel ( uint8_t i , boolean state ) ;
2020-04-12 10:34:14 +00:00
void publishCurrentSetVolume ( ) ;
2020-04-12 12:53:42 +00:00
void publishAllStates ( int pn , String pTopicname , boolean ( * pgetBit ) ( uint8_t ) ) ;
2020-05-03 11:20:45 +00:00
void changeRelaisByNumberTopic ( uint8_t pn , String pTopicPrefix , String pTopic , String pspayload , void ( * psetXChannel ) ( uint8_t , boolean ) ) ;
void setRelaisByNumber ( uint8_t pn , String pTopicPrefix , uint8_t pnumber , bool pstate , void ( * psetXChannel ) ( uint8_t , boolean ) ) ;
2020-04-26 13:30:35 +00:00
float getSetVolume ( ) ;
2020-04-01 09:37:09 +00:00
2020-03-31 15:13:21 +00:00
# define LEDPIN 9 //PB1 = D9 = Pin15
Adafruit_NeoPixel leds = Adafruit_NeoPixel ( 9 , LEDPIN , NEO_GRB + NEO_KHZ800 ) ;
uint8_t wheelpos = 0 ;
# include "Ethernet.h"
# include "PubSubClient.h"
2020-04-03 13:38:59 +00:00
boolean useethernet = true ;
2020-03-31 15:13:21 +00:00
//Ethernet and MQTT
String ip = " " ;
uint8_t mac [ 6 ] = { 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x06 } ;
# define CLIENT_ID "Mixer"
EthernetClient ethClient ;
PubSubClient mqttClient ;
2020-04-12 12:53:42 +00:00
String mqttdevname = " audiomixer/ " ;
2020-03-31 15:13:21 +00:00
2020-04-26 13:30:35 +00:00
bool flag_publishCurrentSetVolume = false ;
2020-04-03 13:38:59 +00:00
2020-03-31 15:13:21 +00:00
long last_send = 0 ;
2020-04-26 13:30:35 +00:00
# define MAXIMUMMQTTSENDINTERVAL 100
2020-04-03 13:38:59 +00:00
# define MQTTRECONNECTDELAY 5000
unsigned long last_mqttreconnectattempt = 0 ;
2020-03-31 15:13:21 +00:00
2020-04-26 13:30:35 +00:00
2020-03-31 15:13:21 +00:00
//Serial
long last_serialdebug = 0 ;
# define INTERVAL_SERIALDEBUG 200
2020-04-26 11:01:59 +00:00
2020-03-31 15:13:21 +00:00
//Inputs
2020-04-01 09:37:09 +00:00
# include "button.h"
2020-03-31 15:13:21 +00:00
# define PIN_BUTTON A3 //A3 = PC3, defining PCx as pin doesnt work
# define PIN_ENCA A2 //A2 = PC2
# define PIN_ENCB A1 //A1 = PC1
2020-04-01 09:37:09 +00:00
Button button_knob ;
2020-03-31 15:13:21 +00:00
//Shift Register 595
//connections: https://www.arduino.cc/en/tutorial/ShiftOut
# define SRLATCH PD4 //D4 = PD4
# define SRCLOCK PD3 //D3 = PD3
# define SRDATA PD2 //D2 = PD2
uint16_t srbits = 0 ;
2020-04-03 13:38:59 +00:00
# define NUMSELECTCHANNELS 8
# define NUMMUTECHANNELS 8
2020-03-31 15:13:21 +00:00
# include <Encoder.h>
Encoder volEnc ( PIN_ENCA , PIN_ENCB ) ;
float encoderMultiplier = 4.0 ;
2020-04-26 13:30:35 +00:00
int volEncVel = 0 ;
2020-03-31 15:13:21 +00:00
//Servo stuff
# define PIN_MOTOR_IN1 PD5 //to L293(pin2) Motor IN1
# define PIN_MOTOR_IN2 PD6 //to L293(pin7) Motor IN2
//#define SRPIN_MOTOR_IN1 1 //L293(pin2) Motor IN1 -- moved to atmega pin
//#define SRPIN_MOTOR_IN2 2 //L293(pin7) Motor IN2 -- moved to atmega pin
2020-04-01 11:01:07 +00:00
uint8_t motorspeed = 0 ;
2020-04-26 13:30:35 +00:00
int _motormove ;
2020-03-31 15:13:21 +00:00
# define PIN_POT A0 //A0 = PC0, reference potentiometer wiper
2020-04-12 10:34:14 +00:00
# define DEADZONE_POTI 10 //maximum allowed error. stop when reached this zone
# define POT_MIN 45 //minimum value pot can reach
# define POT_MAX 950 //maximum value pot can reach
2020-06-27 11:42:22 +00:00
# define POTIFILTER 0.5 //0 to 1. 1 means old value stays forever
# define MAX_MOTOR_PWM 255 //0 to 255. Maximum pwm to output
2020-03-31 15:13:21 +00:00
2020-04-01 11:01:07 +00:00
int poti_set ; //set value, initial value will be read from poti
2020-03-31 15:13:21 +00:00
int poti_read = 0 ; //read value from poti
boolean poti_reachedposition = true ; //set to true if position reached. after that stop turning
2020-04-26 13:30:35 +00:00
int last_potidifference = 0 ;
int potidifference = 0 ; //gets overwritten at start of each motorcheck
2020-03-31 15:13:21 +00:00
//#define MOTOR_STOP(); srWrite(SRPIN_MOTOR_IN1,LOW); srWrite(SRPIN_MOTOR_IN2,LOW);
//#define MOTOR_LEFT(); srWrite(SRPIN_MOTOR_IN1,LOW); srWrite(SRPIN_MOTOR_IN2,HIGH);
//#define MOTOR_RIGHT(); srWrite(SRPIN_MOTOR_IN1,HIGH); srWrite(SRPIN_MOTOR_IN2,LOW);
//#define MOTOR_TURNING() (srRead(SRPIN_MOTOR_IN1) != srRead(SRPIN_MOTOR_IN2))
# define MOTOR_STOP(); digitalWrite(PIN_MOTOR_IN1,LOW); digitalWrite(PIN_MOTOR_IN2,LOW);
# define MOTOR_LEFT(); digitalWrite(PIN_MOTOR_IN1,LOW); digitalWrite(PIN_MOTOR_IN2,HIGH);
# define MOTOR_RIGHT(); digitalWrite(PIN_MOTOR_IN1,HIGH); digitalWrite(PIN_MOTOR_IN2,LOW);
2020-04-01 11:01:07 +00:00
# define MOTOR_LEFT_PWM(); digitalWrite(PIN_MOTOR_IN1,LOW); analogWrite(PIN_MOTOR_IN2,motorspeed);
# define MOTOR_RIGHT_PWM(); analogWrite(PIN_MOTOR_IN1,motorspeed); digitalWrite(PIN_MOTOR_IN2,LOW);
2020-03-31 15:13:21 +00:00
# define MOTOR_TURNING() (digitalRead(PIN_MOTOR_IN1) != digitalRead(PIN_MOTOR_IN2))
//Motorcheck
long last_motorcheck = 0 ;
2020-05-03 11:35:41 +00:00
# define INTERVAL_MOTORCHECK 25 //check motor movement every x ms
2020-04-01 11:01:07 +00:00
//int poti_read_last=0;
//int motor_vel=0; //analog read units per second //TODO: reintroduce into code or remove
//#define MINIMUM_MOTORVEL 20 //minimum velocity motor should turn wenn active
//#define MOTOR_FAILTIME 500 //in ms. if motor did not turn fox x amount of time at least with MINIMUM_MOTORVEL an error will initiate
//long last_motorTooSlow=0; //typically 0
2020-06-27 11:42:22 +00:00
float motorP = 2.0 ;
float motorI = 0.1 ;
2020-05-03 11:35:41 +00:00
float motorD = - 1 ;
2020-04-01 11:01:07 +00:00
float potidifference_integral = 0 ;
2020-05-03 11:04:34 +00:00
//#define MOTORI_ANTIWINDUP 100 //maximum value for (potidifference_integral*motorI). time depends on INTERVAL_MOTORCHECK
# define MOTOR_ANTIWINDUP
2020-06-27 11:42:22 +00:00
# define MOTOR_ANTIWINDUP_RANGE 150 //0 to 1024
//Motor starts moving at about speed=80 (without filter caps)
2020-04-01 11:01:07 +00:00
long last_potidifferenceLow = 0 ;
2020-06-27 11:42:22 +00:00
# define DEADZONETIMEUNTILREACHED 100 //time [ms] poti read value has to be inside of deadzone to set reachedposition flag (and stop regulating)
2020-03-31 15:13:21 +00:00
2020-04-26 11:01:59 +00:00
//Menu system
uint8_t menu_mode = 0 ; //0= volume set mode, 1=mute output selection, 2=output group selection
uint8_t menu_selectedChannel = 0 ;
# define MENU_MAXCHANNEL 7
long last_ledupdate = 0 ;
# define INTERVAL_LEDUPDATE 50
2020-03-31 15:13:21 +00:00
void setup ( ) {
pinMode ( PIN_BUTTON , INPUT_PULLUP ) ;
2020-04-01 09:37:09 +00:00
button_knob = Button ( ) ;
2020-03-31 15:13:21 +00:00
pinMode ( PIN_POT , INPUT ) ;
2020-04-01 11:01:07 +00:00
2020-03-31 15:13:21 +00:00
pinMode ( SRLATCH , OUTPUT ) ;
pinMode ( SRCLOCK , OUTPUT ) ;
pinMode ( SRDATA , OUTPUT ) ;
Serial . begin ( 9600 ) ;
while ( ! Serial ) { } ;
2020-04-12 10:34:14 +00:00
Serial . println ( " Boot " ) ;
2020-03-31 15:13:21 +00:00
leds . begin ( ) ;
2020-04-03 13:38:59 +00:00
leds . clear ( ) ;
2020-03-31 15:13:21 +00:00
for ( uint8_t i = 0 ; i < leds . numPixels ( ) ; i + + ) { //set color of all leds
2020-06-27 11:42:22 +00:00
leds . setPixelColor ( i , leds . Color ( 20 , 20 , 0 ) ) ;
2020-03-31 15:13:21 +00:00
}
leds . show ( ) ;
if ( useethernet )
{
2020-06-14 09:12:13 +00:00
Serial . println ( " DHCP.. " ) ;
2020-03-31 15:13:21 +00:00
if ( Ethernet . begin ( mac ) = = 0 ) { // setup ethernet communication using DHCP
useethernet = false ;
//Unable to configure Ethernet using DHCP
2020-04-12 10:34:14 +00:00
Serial . println ( " DHCP Err " ) ;
2020-03-31 15:13:21 +00:00
delay ( 200 ) ;
//for (;;);
} else {
useethernet = true ;
2020-06-14 09:12:13 +00:00
Serial . print ( " IP: " ) ;
2020-03-31 15:13:21 +00:00
Serial . println ( Ethernet . localIP ( ) ) ;
Serial . println ( ) ;
ip = String ( Ethernet . localIP ( ) [ 0 ] ) ;
ip = ip + " . " ;
ip = ip + String ( Ethernet . localIP ( ) [ 1 ] ) ;
ip = ip + " . " ;
ip = ip + String ( Ethernet . localIP ( ) [ 2 ] ) ;
ip = ip + " . " ;
ip = ip + String ( Ethernet . localIP ( ) [ 3 ] ) ;
//Serial.println(ip);
// setup mqtt client
2020-04-12 10:34:14 +00:00
Serial . println ( " Conf MQTT " ) ;
2020-03-31 15:13:21 +00:00
mqttClient . setClient ( ethClient ) ;
mqttClient . setServer ( " 10.0.0.1 " , 1883 ) ;
2020-04-12 10:34:14 +00:00
Serial . println ( " MQTT ok " ) ;
2020-03-31 15:13:21 +00:00
mqttClient . setCallback ( callback ) ;
}
} else {
2020-04-03 13:38:59 +00:00
Serial . println ( " Eth disabled " ) ;
2020-03-31 15:13:21 +00:00
}
2020-04-01 11:01:07 +00:00
poti_set = analogRead ( PIN_POT ) ;
2020-04-12 12:53:42 +00:00
# ifdef DEBUG
2020-03-31 15:13:21 +00:00
Serial . println ( " Ready " ) ;
2020-04-12 12:53:42 +00:00
# endif
2020-04-12 10:34:14 +00:00
2020-03-31 15:13:21 +00:00
last_send = millis ( ) ;
}
void reconnect ( ) {
// Loop until reconnected
2020-04-03 13:38:59 +00:00
if ( ! mqttClient . connected ( ) ) {
2020-06-27 12:02:51 +00:00
Serial . print ( " MQTT .. " ) ;
2020-03-31 15:13:21 +00:00
// Attempt to connect
if ( mqttClient . connect ( CLIENT_ID ) ) {
Serial . println ( " connected " ) ;
2020-04-12 10:34:14 +00:00
publishCurrentSetVolume ( ) ;
2020-03-31 15:13:21 +00:00
mqttClient . publish ( " audiomixer/ip " , ip . c_str ( ) ) ; //Publish own ip
2020-04-03 13:38:59 +00:00
mqttClient . subscribe ( " audiomixer/volume/set " ) ; //subscribe to /set, republish without /set
2020-06-14 09:12:13 +00:00
mqttClient . subscribe ( " audiomixer/enable/set " ) ; //without range
2020-04-03 13:38:59 +00:00
for ( uint8_t i = 0 ; i < NUMMUTECHANNELS ; i + + ) { //with range
2020-06-14 09:12:13 +00:00
String sub_topic = " audiomixer/enable_ " + String ( i ) + " /set " ;
2020-04-03 13:38:59 +00:00
mqttClient . subscribe ( ( char * ) sub_topic . c_str ( ) ) ;
}
mqttClient . subscribe ( " audiomixer/select/set " ) ; //without range
for ( uint8_t i = 0 ; i < NUMSELECTCHANNELS ; i + + ) { //with range
String sub_topic = " audiomixer/select_ " + String ( i ) + " /set " ;
mqttClient . subscribe ( ( char * ) sub_topic . c_str ( ) ) ;
}
2020-04-12 10:34:14 +00:00
2020-03-31 15:13:21 +00:00
} else {
Serial . print ( " failed, rc= " ) ;
Serial . print ( mqttClient . state ( ) ) ;
}
}
}
void loop ( ) {
long loopmillis = millis ( ) ;
if ( useethernet ) {
if ( ! mqttClient . connected ( ) ) {
2020-04-03 13:38:59 +00:00
if ( loopmillis - last_mqttreconnectattempt > MQTTRECONNECTDELAY ) {
2020-04-12 12:53:42 +00:00
Serial . println ( " Recon. mqtt " ) ;
2020-04-03 13:38:59 +00:00
reconnect ( ) ;
last_mqttreconnectattempt = loopmillis ;
}
2020-03-31 15:13:21 +00:00
}
mqttClient . loop ( ) ;
}
//Serial Input ##############################################
2020-04-01 09:37:09 +00:00
/*For debugging
2020-03-31 15:13:21 +00:00
while ( Serial . available ( ) > 0 ) {
int _value = Serial . parseInt ( ) ;
if ( Serial . read ( ) = = ' \n ' ) {
Serial . print ( " value= " ) ;
Serial . println ( _value ) ;
//poti_set=_value;
//poti_reachedposition=false; //aim for new position
srWrite ( _value , ! srRead ( _value ) ) ;
}
}
2020-04-01 09:37:09 +00:00
*/
2020-03-31 15:13:21 +00:00
//Inputs ###################################################
poti_read = poti_read * POTIFILTER + ( 1.0 - POTIFILTER ) * analogRead ( PIN_POT ) ; //read poti
2020-04-01 11:01:07 +00:00
button_knob . update ( millis ( ) , ! digitalRead ( PIN_BUTTON ) ) ; //Update routine
2020-04-26 11:01:59 +00:00
2020-04-01 09:37:09 +00:00
2020-04-26 11:01:59 +00:00
if ( button_knob . buttonPressed ( ) ) { //short press
switch ( menu_mode ) {
case 0 : //volume
//TODO: implement someting here, muting maybe?
break ;
case 1 : //mute
if ( menu_selectedChannel < = MENU_MAXCHANNEL ) { //inside valid range
2020-05-03 10:38:20 +00:00
//setMuteChannel(menu_selectedChannel,!getMute(menu_selectedChannel)); // mute/unmute menu_selectedChannel
2020-06-14 09:12:13 +00:00
setRelaisByNumber ( NUMMUTECHANNELS , " audiomixer/enable_ " , menu_selectedChannel , ! getMute ( menu_selectedChannel ) , & setMuteChannel ) ; //toggle
2020-04-26 11:01:59 +00:00
} else { //nothing selected
menu_mode = 0 ; //return to volume mode
}
break ;
case 2 : //group selection
if ( menu_selectedChannel < = MENU_MAXCHANNEL ) { //inside valid range
2020-05-03 10:38:20 +00:00
//setSelectionChannel(menu_selectedChannel,!getSelection(menu_selectedChannel)); // toggle selection menu_selectedChannel
2020-05-03 11:20:45 +00:00
setRelaisByNumber ( NUMSELECTCHANNELS , " audiomixer/select_ " , menu_selectedChannel , ! getSelection ( menu_selectedChannel ) , & setSelectionChannel ) ; //toggle
2020-04-26 11:01:59 +00:00
} else { //nothing selected
menu_mode = 0 ; //return to volume mode
}
break ;
}
} else if ( button_knob . buttonHold ( ) ) { //long press
switch ( menu_mode ) {
case 0 : //volume
menu_mode = 1 ; //change to mute select mode
break ;
case 1 : //mute
menu_mode = 2 ; //change to output group select mode. (hold button a second time)
break ;
case 2 : //group selection
menu_mode = 1 ; //change back to mute select mode
break ;
}
2020-03-31 15:13:21 +00:00
}
2020-04-01 09:37:09 +00:00
2020-03-31 15:13:21 +00:00
//Read Encoder to velocity "volEncVel"
2020-04-26 13:30:35 +00:00
2020-03-31 15:13:21 +00:00
int _volEnc = volEnc . read ( ) ;
if ( _volEnc ! = 0 ) { //encoder moved
2020-04-26 13:30:35 +00:00
volEncVel + = _volEnc ;
2020-03-31 15:13:21 +00:00
volEnc . write ( 0 ) ; //reset value
}
//Input Handling
if ( volEncVel ! = 0 ) { //knob moved
2020-04-26 11:01:59 +00:00
switch ( menu_mode ) {
case 0 : //volume
poti_set + = volEncVel * encoderMultiplier ; //change poti set value
poti_set = constrain ( poti_set , POT_MIN , POT_MAX ) ;
poti_reachedposition = false ;
2020-04-26 13:30:35 +00:00
flag_publishCurrentSetVolume = true ;
volEncVel = 0 ; //reset vel for next loop
2020-04-26 11:01:59 +00:00
break ;
case 1 : case 2 : //mute or group selection
menu_selectedChannel + = 127 ; //offset to compensate negative values
2020-04-26 13:30:35 +00:00
menu_selectedChannel + = volEncVel / 2 ; //every encoder detend is +-2
2020-04-26 11:01:59 +00:00
if ( menu_selectedChannel < 127 ) {
menu_selectedChannel = 127 ; //lower limit (0)
2020-04-26 13:30:35 +00:00
} else if ( menu_selectedChannel > 127 + MENU_MAXCHANNEL + 1 ) { //max channel and one extra for "nothing selected"
2020-04-26 11:01:59 +00:00
menu_selectedChannel = 127 + MENU_MAXCHANNEL + 1 ; //upper limit
}
menu_selectedChannel - = 127 ; //take out offset
2020-04-26 13:30:35 +00:00
if ( volEncVel / 2 ! = 0 ) { //if value change was at least one detend
volEncVel = 0 ; //reset vel for next loop
}
2020-04-26 11:01:59 +00:00
break ;
}
2020-03-31 15:13:21 +00:00
}
2020-04-26 13:30:35 +00:00
if ( flag_publishCurrentSetVolume & & loopmillis - last_send > MAXIMUMMQTTSENDINTERVAL ) {
flag_publishCurrentSetVolume = false ;
last_send = loopmillis ;
publishCurrentSetVolume ( ) ;
}
2020-04-12 13:03:37 +00:00
//Motor Movement Routine #################
2020-04-01 11:01:07 +00:00
2020-04-26 13:30:35 +00:00
if ( loopmillis - last_motorcheck > INTERVAL_MOTORCHECK )
2020-04-01 11:01:07 +00:00
{
2020-04-26 13:30:35 +00:00
last_motorcheck = loopmillis ;
last_potidifference = potidifference ; //save last difference
potidifference = poti_set - poti_read ; //positive means poti needs to be moved higher. max poti value is 1023
if ( poti_reachedposition ) {
motorspeed = 0 ;
potidifference_integral = 0 ;
MOTOR_STOP ( ) ;
} else { //not reached position
_motormove = 0 ; //negative: move left, positive: move right. abs value: speed. 0 <= abs(_motormove) <= 255
2020-05-03 11:04:34 +00:00
2020-04-26 13:30:35 +00:00
potidifference_integral + = potidifference * motorI ;
2020-05-03 11:04:34 +00:00
# ifdef MOTOR_ANTIWINDUP
if ( abs ( potidifference ) > MOTOR_ANTIWINDUP_RANGE ) { //outside integral range
potidifference_integral = 0 ; //reset
}
# endif
//potidifference_integral=constrain(potidifference_integral,-MOTORI_ANTIWINDUP,MOTORI_ANTIWINDUP); //constrain
2020-04-26 13:30:35 +00:00
_motormove = potidifference * motorP + potidifference_integral + motorD * ( last_potidifference - potidifference ) ;
motorspeed = constrain ( abs ( _motormove ) , 0 , MAX_MOTOR_PWM ) ;
if ( poti_read < = POT_MIN & & _motormove < 0 ) { //stop motor if soft endstops reached and wants to turn that way
MOTOR_STOP ( ) ;
2020-04-01 11:01:07 +00:00
potidifference_integral = 0 ;
2020-04-26 13:30:35 +00:00
_motormove = 0 ;
} else if ( poti_read > = POT_MAX & & _motormove > 0 ) { //stop motor if soft endstops reached and wants to turn that way
2020-04-01 11:01:07 +00:00
MOTOR_STOP ( ) ;
2020-04-26 13:30:35 +00:00
potidifference_integral = 0 ;
_motormove = 0 ;
} else { //no endstop reached
if ( _motormove < 0 ) {
MOTOR_LEFT_PWM ( ) ;
} else if ( _motormove > 0 ) {
MOTOR_RIGHT_PWM ( ) ;
} else {
2020-04-01 11:01:07 +00:00
MOTOR_STOP ( ) ;
}
2020-04-26 13:30:35 +00:00
}
2020-04-01 11:01:07 +00:00
2020-04-26 13:30:35 +00:00
if ( ( potidifference > 0 & & last_potidifference < 0 ) | | ( potidifference < 0 & & last_potidifference > 0 ) ) { //different signs. potidifference has crossed 0. set value overshoot
potidifference_integral = 0 ; //reset integral to stop further overshoot
}
/*
Serial . print ( " diff= " ) ;
Serial . print ( potidifference ) ;
Serial . print ( " iVal= " ) ;
Serial . print ( potidifference_integral ) ;
Serial . print ( " dVal= " ) ;
Serial . print ( ( last_potidifference - potidifference ) * motorD ) ;
Serial . print ( " motormove= " ) ;
Serial . print ( _motormove ) ;
if ( poti_reachedposition ) {
Serial . print ( " ! " ) ;
}
Serial . println ( " " ) ;
*/
if ( abs ( potidifference ) < DEADZONE_POTI ) {
if ( last_potidifferenceLow = = 0 ) {
last_potidifferenceLow = loopmillis ; //save millis first time entered deadzone
}
if ( loopmillis - last_potidifferenceLow > DEADZONETIMEUNTILREACHED ) {
poti_reachedposition = true ;
2020-04-01 11:01:07 +00:00
}
2020-04-26 13:30:35 +00:00
} else {
last_potidifferenceLow = 0 ;
2020-04-01 11:01:07 +00:00
}
2020-04-26 13:30:35 +00:00
2020-04-01 11:01:07 +00:00
}
}
2020-04-26 13:30:35 +00:00
2020-04-01 11:01:07 +00:00
2020-03-31 15:13:21 +00:00
2020-04-26 11:01:59 +00:00
if ( loopmillis > last_ledupdate + INTERVAL_LEDUPDATE ) {
last_ledupdate = loopmillis ;
2020-06-14 09:27:50 +00:00
2020-04-26 11:01:59 +00:00
switch ( menu_mode ) {
case 0 : //volume
2020-04-26 13:30:35 +00:00
if ( poti_reachedposition ) { //idle
2020-06-14 09:27:50 +00:00
for ( uint8_t i = 0 ; i < leds . numPixels ( ) - 1 ; i + + ) { //set color of all front leds
2020-04-26 13:30:35 +00:00
leds . setPixelColor ( i , Wheel ( wheelpos + i * 10 ) ) ;
}
} else { //motor is currently moving
leds . clear ( ) ;
leds . setPixelColor ( min ( getSetVolume ( ) * 8 / 100 , 7 ) , Wheel ( wheelpos ) ) ; //show volume as bargraph
2020-04-26 11:01:59 +00:00
}
2020-04-26 13:30:35 +00:00
2020-04-26 11:01:59 +00:00
wheelpos + = 1 ;
break ;
case 1 : case 2 : //group selection //mute
//show selected channel
leds . clear ( ) ;
uint8_t _r = 0 ;
uint8_t _g = 0 ;
uint8_t _b = 0 ;
2020-04-26 13:30:35 +00:00
for ( uint8_t i = 0 ; i < = MENU_MAXCHANNEL ; i + + ) { //set color of all channel leds
2020-04-26 11:01:59 +00:00
if ( menu_mode = = 1 ) { //in mute mode
2020-04-26 13:30:35 +00:00
if ( getMute ( i ) ) { //not muted
2020-06-14 09:27:50 +00:00
_r = 0 ; _g = 50 ; _b = 0 ; //Output is enabled
2020-04-26 11:01:59 +00:00
} else { //muted
2020-06-14 09:27:50 +00:00
_r = 10 ; _g = 0 ; _b = 0 ; //Output is mued
2020-04-26 11:01:59 +00:00
}
} else if ( menu_mode = = 2 ) { //in selection mode
if ( ! getSelection ( i ) ) { //Selected A
_r = 50 ; _g = 50 ; _b = 50 ;
} else { //Selected B
_r = 50 ; _g = 0 ; _b = 0 ;
}
}
if ( menu_selectedChannel = = i ) { //this item is currently selected
_r * = 4 ; //make color brighter
_g * = 4 ;
_b * = 4 ;
}
if ( menu_selectedChannel = = MENU_MAXCHANNEL + 1 ) { //nothing selected
2020-04-26 13:30:35 +00:00
_r * = 4 ; //make color brighter
_g * = 4 ;
_b * = 4 ;
2020-04-26 11:01:59 +00:00
}
leds . setPixelColor ( i , leds . Color ( _r , _g , _b ) ) ;
}
break ;
}
2020-06-14 09:27:50 +00:00
//Volume LED
leds . setPixelColor ( leds . numPixels ( ) - 1 , Wheel ( wheelpos ) ) ; //volume poti led
2020-04-26 11:01:59 +00:00
leds . show ( ) ;
}
2020-03-31 15:13:21 +00:00
2020-04-26 13:30:35 +00:00
/*
2020-03-31 15:13:21 +00:00
if ( loopmillis > last_serialdebug + INTERVAL_SERIALDEBUG ) {
last_serialdebug = loopmillis ;
Serial . print ( " set= " ) ;
Serial . print ( poti_set ) ;
Serial . print ( " is= " ) ;
Serial . print ( poti_read ) ;
2020-04-26 13:30:35 +00:00
//Serial.print(" mspeed=");
//Serial.print(motorspeed);
Serial . print ( " motormove= " ) ;
Serial . print ( _motormove ) ;
2020-04-12 10:34:14 +00:00
Serial . print ( " iVal= " ) ;
2020-04-01 11:01:07 +00:00
Serial . print ( potidifference_integral ) ;
2020-04-12 10:34:14 +00:00
if ( poti_reachedposition ) {
Serial . print ( " ! " ) ;
2020-03-31 15:13:21 +00:00
}
2020-04-12 10:34:14 +00:00
Serial . println ( " " ) ;
2020-03-31 15:13:21 +00:00
}
2020-04-26 13:30:35 +00:00
*/
2020-03-31 15:13:21 +00:00
2020-04-26 11:01:59 +00:00
if ( loopmillis % 5001 = = 0 ) { //TODO: remove when working
2020-04-12 10:34:14 +00:00
Serial . println ( loopmillis ) ; //alive print. for debugging
}
2020-03-31 15:13:21 +00:00
}
void callback ( char * topic , byte * payload , unsigned int length ) {
payload [ length ] = ' \0 ' ; //add end of string character
2020-04-03 13:38:59 +00:00
String spayload = String ( ( char * ) payload ) ;
2020-06-27 12:02:51 +00:00
Serial . print ( " msg: " ) ;
2020-03-31 15:13:21 +00:00
Serial . print ( topic ) ;
2020-04-03 13:38:59 +00:00
for ( unsigned int i = 0 ; i < length ; i + + ) {
2020-03-31 15:13:21 +00:00
Serial . print ( ( char ) payload [ i ] ) ;
}
Serial . println ( ) ;
2020-04-12 12:53:42 +00:00
2020-04-03 13:38:59 +00:00
if ( String ( topic ) . equals ( " audiomixer/volume/set " ) ) {
float _floatvalue = spayload . toFloat ( ) ;
_floatvalue = constrain ( _floatvalue , 0.0 , 100.0 ) ;
2020-03-31 15:13:21 +00:00
poti_set = constrain ( map ( _floatvalue , 0.0 , 100.0 , POT_MIN , POT_MAX ) , POT_MIN , POT_MAX ) ; //set new poti position
poti_reachedposition = false ; //aim for new position
2020-04-03 13:38:59 +00:00
2020-04-12 10:34:14 +00:00
publishCurrentSetVolume ( ) ;
2020-04-03 13:38:59 +00:00
2020-06-14 09:12:13 +00:00
} else if ( String ( topic ) . equals ( " audiomixer/enable/set " ) ) { //withouth range
2020-04-12 10:34:14 +00:00
//Serial.print("Mute string="); Serial.println(spayload);
2020-04-03 13:38:59 +00:00
uint16_t ipayload = spayload . toInt ( ) ;
if ( spayload . equalsIgnoreCase ( " false " ) ) {
2020-06-14 09:12:13 +00:00
setMuteInt ( 0 ) ; //all muted
2020-04-03 13:38:59 +00:00
} else if ( spayload . equalsIgnoreCase ( " true " ) ) {
setMuteInt ( pow ( 2 , NUMMUTECHANNELS ) - 1 ) ; //all muted
} else if ( ipayload > = 0 & & ipayload < ( ( uint16_t ) 1 < < NUMMUTECHANNELS ) ) { //in range
setMuteInt ( ipayload ) ; //set bits directly
}
2020-04-12 13:03:37 +00:00
2020-06-14 09:12:13 +00:00
publishAllStates ( NUMMUTECHANNELS , " enable_ " , & getMute ) ;
2020-04-12 12:53:42 +00:00
} else if ( String ( topic ) . equals ( " audiomixer/select/set " ) ) { //withouth range
//Serial.print("Select string="); Serial.println(spayload);
uint16_t ipayload = spayload . toInt ( ) ;
if ( spayload . equalsIgnoreCase ( " false " ) ) {
setSelectionInt ( 0 ) ; //all select to NC
} else if ( spayload . equalsIgnoreCase ( " true " ) ) {
setSelectionInt ( pow ( 2 , NUMSELECTCHANNELS ) - 1 ) ; //all select to NO
} else if ( ipayload > = 0 & & ipayload < ( ( uint16_t ) 1 < < NUMSELECTCHANNELS ) ) { //in range
setSelectionInt ( ipayload ) ; //set bits directly
2020-04-03 13:38:59 +00:00
}
2020-04-12 12:53:42 +00:00
publishAllStates ( NUMSELECTCHANNELS , " select_ " , & getSelection ) ;
2020-06-14 09:12:13 +00:00
} else if ( String ( topic ) . startsWith ( " audiomixer/enable_ " ) ) { //with range
changeRelaisByNumberTopic ( NUMMUTECHANNELS , " audiomixer/enable_ " , topic , spayload , & setMuteChannel ) ;
2020-04-03 13:38:59 +00:00
} else if ( String ( topic ) . startsWith ( " audiomixer/select_ " ) ) {
2020-05-03 11:20:45 +00:00
changeRelaisByNumberTopic ( NUMSELECTCHANNELS , " audiomixer/select_ " , topic , spayload , & setSelectionChannel ) ;
2020-03-31 15:13:21 +00:00
}
2020-04-12 12:53:42 +00:00
2020-03-31 15:13:21 +00:00
}
2020-04-01 09:37:09 +00:00
void srWrite ( uint8_t pbit , boolean state ) { //change bit to state
2020-03-31 15:13:21 +00:00
if ( state = = true ) {
2020-04-01 09:37:09 +00:00
srbits | = 1UL < < pbit ; //set bit
2020-03-31 15:13:21 +00:00
} else {
2020-04-01 09:37:09 +00:00
srbits & = ~ ( 1UL < < pbit ) ; //clear bit
2020-03-31 15:13:21 +00:00
}
2020-04-03 13:38:59 +00:00
srShiftOut ( ) ;
}
void srShiftOut ( ) {
2020-03-31 15:13:21 +00:00
digitalWrite ( SRLATCH , LOW ) ;
2020-06-27 12:02:51 +00:00
shiftOut ( SRDATA , SRCLOCK , LSBFIRST , srbits > > 8 ) ;
shiftOut ( SRDATA , SRCLOCK , LSBFIRST , srbits ) ;
2020-03-31 15:13:21 +00:00
digitalWrite ( SRLATCH , HIGH ) ;
}
2020-04-01 09:37:09 +00:00
boolean srRead ( uint8_t pbit ) { //get state at bit
return ( srbits > > pbit ) & 1U ;
2020-03-31 15:13:21 +00:00
}
uint32_t Wheel ( byte WheelPos ) {
WheelPos = 255 - WheelPos ;
if ( WheelPos < 85 ) {
return leds . Color ( 255 - WheelPos * 3 , 0 , WheelPos * 3 ) ;
}
if ( WheelPos < 170 ) {
WheelPos - = 85 ;
return leds . Color ( 0 , WheelPos * 3 , 255 - WheelPos * 3 ) ;
}
WheelPos - = 170 ;
return leds . Color ( WheelPos * 3 , 255 - WheelPos * 3 , 0 ) ;
}
2020-04-03 13:38:59 +00:00
2020-06-14 09:12:13 +00:00
void setMuteInt ( uint8_t i ) { //low is muted
2020-04-03 13:38:59 +00:00
uint16_t mask = ( ( ( uint16_t ) 1 < < ( NUMMUTECHANNELS ) ) - 1 ) < < NUMSELECTCHANNELS ;
srbits = ( ( i < < NUMSELECTCHANNELS ) & mask ) | ( srbits & ~ mask ) ;
srShiftOut ( ) ;
}
2020-04-26 13:30:35 +00:00
boolean getMute ( uint8_t pbit ) { //low is muted
2020-04-03 13:38:59 +00:00
return srbits & ( 1 < < ( pbit + NUMSELECTCHANNELS ) ) ; //check bit at position
}
void setSelectionInt ( uint8_t i ) {
uint16_t mask = ( ( ( uint16_t ) 1 < < ( NUMMUTECHANNELS ) ) - 1 ) < < NUMSELECTCHANNELS ;
srbits = ( srbits & mask ) | ( i & ~ mask ) ;
srShiftOut ( ) ;
}
boolean getSelection ( uint8_t pbit ) {
return srbits & ( 1 < < pbit ) ; //check bit at position
}
void setSelectionChannel ( uint8_t i , boolean state ) {
if ( i < NUMSELECTCHANNELS ) {
srWrite ( i , state ) ;
}
}
void setMuteChannel ( uint8_t i , boolean state ) {
if ( i < NUMMUTECHANNELS ) {
srWrite ( i + NUMSELECTCHANNELS , state ) ; //offset. selection is first shift register
}
2020-04-12 10:34:14 +00:00
}
void publishCurrentSetVolume ( )
{
2020-04-26 13:30:35 +00:00
float _setpercentage = getSetVolume ( ) ;
2020-04-12 10:34:14 +00:00
char pub_payload [ 8 ] ; // Buffer big enough for 7-character float
dtostrf ( _setpercentage , 1 , 2 , pub_payload ) ;
mqttClient . publish ( " audiomixer/volume " , pub_payload ) ;
Serial . print ( " pub= " ) ; Serial . println ( _setpercentage ) ;
2020-04-12 12:53:42 +00:00
}
void publishAllStates ( int pn , String pTopicname , boolean ( * pgetBit ) ( uint8_t ) ) {
for ( uint8_t i = 0 ; i < pn ; i + + ) {
String pub_topic = mqttdevname + pTopicname + String ( i ) ;
boolean _state = pgetBit ( i ) ;
if ( _state ) {
mqttClient . publish ( ( char * ) pub_topic . c_str ( ) , " true " ) ;
} else {
mqttClient . publish ( ( char * ) pub_topic . c_str ( ) , " false " ) ;
}
}
}
2020-05-03 11:20:45 +00:00
void changeRelaisByNumberTopic ( uint8_t pn , String pTopicPrefix , String pTopic , String pspayload , void ( * psetXChannel ) ( uint8_t , boolean ) )
2020-04-12 12:53:42 +00:00
{
uint8_t _index = 255 ;
for ( uint8_t i = 0 ; i < pn & & _index = = 255 ; i + + ) {
if ( String ( pTopic ) . equals ( pTopicPrefix + String ( i ) + " /set " ) ) {
_index = i ;
}
}
if ( _index = = 255 ) {
Serial . println ( " Index OOR " ) ;
} else { //index ok
2020-05-03 11:20:45 +00:00
bool _state = false ;
if ( pspayload . equalsIgnoreCase ( " true " ) ) {
_state = true ;
2020-04-12 12:53:42 +00:00
}
2020-05-03 11:20:45 +00:00
setRelaisByNumber ( pn , pTopicPrefix , _index , _state , psetXChannel ) ;
2020-04-12 12:53:42 +00:00
}
2020-04-26 13:30:35 +00:00
}
2020-05-03 11:20:45 +00:00
void setRelaisByNumber ( uint8_t pn , String pTopicPrefix , uint8_t pnumber , bool pstate , void ( * psetXChannel ) ( uint8_t , boolean ) )
{
String pub_topic = pTopicPrefix + String ( pnumber ) ;
psetXChannel ( pnumber , pstate ) ;
String _mqttpayload = " false " ;
if ( pstate ) { //True
_mqttpayload = " true " ;
}
mqttClient . publish ( ( char * ) pub_topic . c_str ( ) , ( char * ) _mqttpayload . c_str ( ) ) ; //Publish
}
2020-04-26 13:30:35 +00:00
float getSetVolume ( ) {
return map ( poti_set , POT_MIN , POT_MAX , 0.0 , 100.0 ) ; //get percentage from set poti value
2020-04-03 13:38:59 +00:00
}