' ********************************************************************
' *                    PIC firmware for <Gesta>                      *
' *                           Hub board                              *
' *                  coded by dr.Godfried-Willem Raes                *
' * https://www.logosfoundation.org/instrum_gwr/gesta/picworks       *
' *                  source code development directory:              *
' *                         Gesta_Hub.bas                            *  
' *                         Version 1.0                              *
' *                Proton Compiler version 3.5.8.1                   *
' ********************************************************************
' hardware: single side PCB 08.10.2025 - film for <Kazumi> used
' -------------------------------------------------------------------
' 15.10.2026: Start coding for <Gesta>
' 16.10.2026: Nmap lookup introduced.
' 19.10.2026: The board is the same as that used for Kazumi.
' 20.10.2026: motor code stemming from <Ekla> removed. Lite code debugged.
'             PCB finished.

Include "18F2620.inc"      'version for the <Gesta> board.         (40MHz)
'Include "18F2520.inc"     'also possible.                       (40MHz)
Clear

' Mapping defines for midi-events on pin outputs and inputs:
' zero cross opto-relay's
    $define Power_Relay      PORTA.4     ' o.k. for <Gesta>
  
' Mosfet outputs:  
    $define Lite120          PORTC.0   ' 120  - kan knipperen
    $define Lite121          PORTC.1   ' can pwm HPWM2        
    $define Lite122          PORTC.3   ' 122  - kan knipperen
    $define Lite123          PORTC.4   ' 123  - kan knipperen 
    $define Display          PORTA.5   ' Logos-display   noot 124
    $define Lite127          PORTC.2   ' can pwm HPWM1       127               
' inputs:    
    $define sensor1        PORTA.0     ' not used 
    $define sensor2        PORTA.1     ' not used  
' LED's:    
     $define GreenLed      PORTB.4       ' on/off   
     $define Debug_Led     PORTB.5       ' for testing - red led - watchdog
     $define Loopspeed     PORTB.0       ' B.3 and B.4 are NC
    
Declare All_Digital = True        ' makes all analog pins, digital for I/O
Clear SSPCON1.5                   ' make sure RC3 is free for use.
' configure the input and output pins:
TRISA = %11000011     'bits set to 0 are output, 1 = input  - bits 6 and 7 are the clock!
TRISB = %11100000     'bits 6 and 7 are for the ICP, bit 5 is the red LED
TRISC = %11000000     'RC6 en RC7 zijn USART  I/O and must be set to input     
Input sensor1        
Input sensor2     
   
'constant definitions:
Symbol fPwm  = 21000   ' PWMminF
    ' midi note mapping
Symbol Lite120_note = 120   
Symbol Lite121_note = 121    ' pwm2
Symbol Lite122_note = 122  
Symbol Lite123_note = 123 
Symbol Display_note = 124     
Symbol Lite127_note = 127    ' pwm1    

Symbol lowest_note = 60 
Symbol highest_note = 108 
Symbol NrTasks = 4 '3 '7
Symbol LastTask = NrTasks - 1

' constants:
Symbol True = %1
Symbol False = %0
'Symbol Period10 = 53039  ' for 10ms pulses motor control slow pwm , value for TIM3
'Symbol Period5 = 59288   ' for 5ms pulses motor control slow pwm
'Symbol Period25 = 62412  ' for 2.5ms pulses 4 Hz or period = 250ms.  3124 ticks
'Symbol Period125 = 63974 ' for 1.25ms pulses, 8Hz or period = 125ms. 1562 ticks

'Symbol Period_default = Period5  
Symbol CC19_default = 110      ' new 27.08.2026
  

'initialisations for the midi input parser:
Symbol Midichannel = 3                         ' Gesta channel - only on hubboard!
Symbol MidiLowNoteChannel = 0                  ' output channels
Symbol MidiHighNoteChannel = 1
Symbol MidiLowDampChannel = 2
Symbol MidiHighDampChannel = 3
Symbol NoteOff_Status = 128 + Midichannel      ' 2 bytes follow
Symbol NoteOn_Status  = 144 + Midichannel
Symbol Keypres_Status = 160 + Midichannel
Symbol Control_Status = 176 + Midichannel
Symbol ProgChange_Status = 192 + Midichannel   ' 1 byte message
Symbol Aftertouch_Status = 208 + Midichannel   ' 1 byte follows
Symbol Pitchbend_Status  = 224 + Midichannel   ' lsb msb follow
' Setup the USART
Declare Hserial_Baud = 31250                   ' Set baud rate for the USART to MIDI specs.
Declare Hserial_TXSTA = 0x24                   ' instead of the normal 0x20 - ?? 0x24               
Declare Hserial_Clear = On                     ' should clear on errors. Bytes get lost of course...
' Create variables:
    Dim Cnt As Dword System                    ' 1 cnt-unit ought to be 24 us.
    Dim CntHw As Cnt.Word1                     ' used in the timer0 interrupt, to create a 32 bit timer
    Dim CntLw As TMR0L.Word                    ' this is the trick to read both TMR0L and TMR0H
                                               ' it makes Cntlw the low word of cnt
                                               ' We still have to copy the contents of Lw to Cnt
    Dim time As Cnt                            ' was : Dword  'frozen copy of Cnt
    Dim MaxTim As time.28                      ' overflow flag used to reset timer and to power down
                                               ' time.30 is 7 hours.
                                               ' time.29 is 214 minutes or 3h30
                                               ' time.28 would be 107 minutes - checked and found o.k.
 
    Dim T3 As Dword                            ' make 32-bit timer
    Dim Tim3Hw As T3.Word1 
   ' Dim MotTim As Byte                         ' for duty cycle control, alternating on and off value for motor 
   ' Dim MotCnt As Byte                         ' PWM counter increments in 10ms units in the tim3 irq 
    Dim Tim3 As TMR3L.Word                     ' same trick for timer3 , 16-bits
    
    Dim Bytein  As Byte System                    ' midi byte read from buffer
    Dim StBit As Bytein.7                         ' highest bit of ByteIn
    Dim i As Byte System                          ' general purpose counter
    ' midi variables
    Dim statusbyte As Byte System
    Dim noteUit As Byte System                    ' note off + release value
    Dim release As Byte System
    Dim noteAan As Byte System                    ' note on + release value
    Dim velo As Byte System
    Dim notePres As Byte System                   ' note pressure + pressure value
    Dim pres As Byte System
    Dim Ctrl As Byte System                       ' continuous controller + value
    Dim value As Byte System
    Dim prog As Byte System                       ' program change + program-byte
    Dim aft  As Byte System                       ' channel aftertouch
    Dim pblsb As Byte System                      ' pitch bend lsb
    Dim pbmsb As Byte System                      ' pitch bend msb
    
    Dim note As Byte System
    Dim kanaal As Byte System
    Dim CC64 As Byte System                     ' sustain pedal
    Dim CC19 As Byte System                     ' release controller - not forwarded!    
    Dim CC66 As Byte System                     ' global on/off switch
        Dim PowerOn As CC66.0                   ' handled on the hub board.
       ' Dim Mtog As CC66.2                           ' used in timer 3 irq
    Dim CC69 As Byte System                     ' lite automation
   ' Dim MotPer As Word System                    ' 1.25ms, 2.5ms, 5 ms or 10 ms value for Tim3

   ' Dim CC10 As Byte System               ' motor speed  0-100
   ' Dim oldMotor As Byte
   ' Dim CC11 As Byte                      ' hub to synth messaging for legato
   ' Dim legato_auto As CC11.0             ' 0 = automatic legato off, 1 = on
   ' Dim legato_force As CC11.1            ' 0 = no forcing, 1 = suppress note off's

   ' Dim Period As Word System             ' controller 67
   ' Dim CC67 As Byte     
           
   ' Dim MotorFlags As Byte System
   '     Dim MotorOn As MotorFlags.0         ' 25.10.2025
   '     Dim MotWait As MotorFlags.1         ' if 1, motor wait timer is running.
     
    Dim TimVals[NrTasks] As Dword         ' lijst met timer waarden - de kleinste is eerst aan de beurt
    Dim Nxt As Dword System               ' waarde voor de eerstvolgende timer
    Dim idx As Byte System                ' index voor de eerstvolgende timer 
'    Dim Playing_note As Byte
    Dim j As Float
    ' for light automation:
    Dim NrNotes As Word
    Dim NrLowNotes As NrNotes.Byte0    ' aantal klinkende noten op de lage module (0-25)
    Dim NrHighNotes As NrNotes.Byte1   ' aantal klinkende noten op de hoge module (0-24)
    Dim Lites As Byte                  ' bit-flags
    Dim Pres0 As Byte
    Dim Pres1 As Byte
    Dim Pres2 As Byte
    Dim Pres3 As Byte
    Dim Pres4 As Byte
    
'-----------------------------------------------------------------------------------------
' Load the USART Interrupt handler and buffer read subroutines into memory
Dim Ringbuffer[256] As Byte           ' Array for holding received characters
Include "Gesta_Hub_Irq.inc"                ' for UART,Timer0, Timer3 Interrupt
Dim Dur5[128] As Word                    ' duration lookup for lite flashing repetitions
'Dim Toff[128] As Byte 'Dword             ' lookup for motor slow pwm
'Dim Ton[128] As Byte  'Dword
Dim Nmap[128] As Byte

Low Power_Relay
High Debug_Led        ' we must make sure the port is set to output
Clear CC66            ' all flags
Clear CC64            ' sostenuto pedal
'Clear MotorFlags      ' 2 flags

' Main program starts here:
MAIN:
    Low GreenLed
    Low Lite120
    Low Lite121
    Low Lite122
    Low Lite123   
    Low Lite127  
    Low Display   
    Clear NrNotes
    Set TimVals                      ' no timers running on startup
    
    Init_Usart_Interrupt ()          ' Initiate the USART serial buffer interrupt
                                     ' this procedure is in the include file
    Clear_Serial_Buffer ()           ' Clear the serial buffer and reset its pointers
                                     ' in the include as well
                                     ' Configure Timer0 for:
'                                      Clear TMR0L and TMR0H registers
'                                      Interrupt on Timer0 overflow
'                                      16-bit operation
'                                      Internal clock source  40MHz
'                                      1:256 Prescaler : thus 40MHz / 256 = 156.250kHz
'                                      6.4 us per clock-tick
         Clear T1CON
         Clear IntConBits_T0IF      ' clear interrupt flag
         Set INTCONBITS_T0IE        ' enable interrupt on overflow 
         T0CON = %10000111           ' bit 7 = enable/disable
                                     ' bit 6 = 1=8 bit, 0=16 bit
                                     ' bit 5 = 1 pin input, 0= Internal Clk0
                                     ' bit 4 = HL or LH transition when bit5 =1 
                                     ' bit 3 = 1= bypass prescaler, 0= input from prescaler
                                     ' bit 2-0 = prescaler select: 111= 1:256
                                     ' Setup the High priorities for the interrupts
' TIMER1: if enabled, all midi-in is blocked, so it must interfere with the UART                                     
             ' Configure Timer1 for:
'                        Clear TMR1L and TMR1H registers
'                        Interrupt on Timer1 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'    OpenTimer1(TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8)    ' dit kompileert o.k.                               
' TIMER2: if enabled, the UART stops working...
'    Opentimer2 (Timer_Int_On & T2_POST_1_16 & T2_PS_1_16)     ' dit lukt... maar de timer is nodig voor de UART...
' TIMER3:                                                              
' Configure Timer3 for:                     
'                        Interrupt on Timer3 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'                        Dont sync external clock input

'Period = Period_default      ' controller 67

 ' doing it this way seems to work:
    Clear T3CON
    Clear PIR2BITS_TMR3IF   ' clear IRQ flag
    Set PIE2BITS_TMR3IE     ' irq on
    Clear Tim3              ' Clear TMR3L And TMR3H registers
   ' Tim3 = Period           ' period5 = 59288            ' 5 ms - measured and found correct.
    Set RCONbits_IPEN       ' Enable priority interrupts
    Clear IPR2bits_TMR3IP   ' Set Timer3 as a low priority interrupt source                   
    ' we can also set T3Con in one instruction as:
    T3CON = %10110001               ' oef, now it works...
                            ' bit 7 = 16 bit mode
                            ' bit 6,3 = 0, 0
                            ' bit 5,4 = 1:8 prescale
                            ' bit 2 = 0
                            ' bit 1 = 0 Internal clock = Fosc/4
                            ' bit 0 : 1= enable timer 3, 0= disable
                            ' maximum count = 52.42ms, 1 tick =0.8uS, freq.=19Hz
    ' Set up priority interrupts.
  '  IPR1bits_TMR1IP = 0    ' Set Timer1 as a low priority interrupt source
  '  INTCONbits_PEIE = 1    ' Enable peripheral interrupts
  '  INTCONbits_GIE = 1     ' Enable global interrupts                        
    Set PIE2Bits_TMR3IE     ' enable irq
    Set T3CON.0           ' enable timer 3
Dur_Lookup ()
'Motor_PWM_Lookup ()                     
Note_Map ()

idx = SortTimers () 
Set CC69                     ' by default light automation is ON
'CC10 = CC10_default
'CC11 = CC11_default

    ' send a power off to the pulse-hold boards and the generator board:
HRSOut Control_Status, 66, 0               

    ' start the main program loop:
Do                                   
        Cnt.Word0 = CntLw            ' read timer 0
                                     ' now time is an alias for Cnt
        Bytein = GetMidiIn ()        ' Read data from the serial buffer, with no timeout
                                     ' Start the midi parser.                                                                                   
        Midi_Parse:
            If Bytein > Control_Status Then    ' here higher statusses are not implemented.
                If Bytein > 253 Then       '254 = midiclock, 255= reset
                                           'midiclock can interrupt all other msg's...
                                           '255 had to be intercepted since thats what we
                                           'get when no new byte flows in.
                Else
                    Clear statusbyte       'reset the status byte
                End If
                GoTo Check_Timers          'throw away...
            EndIf
            If StBit =1 Then               'should be faster than If Bytein > 127 Then
                                           'status byte received, bit 7 is set
                Clear statusbyte           'if on another channel, the statusbyte needs a reset
                Select Bytein              'eqv to Select case ByteIn
                       Case NoteOff_Status
                            statusbyte = Bytein
                            Set noteUit          'reset value. Cannot be 0 !!!
                            Set release          '0 is a valid midi note!
                       Case NoteOn_Status
                            statusbyte = Bytein
                            Set noteAan             '= 255
                            Set velo                '= 255
                       Case Keypres_Status          
                            statusbyte = Bytein
                            Set notePres 
                            Set pres 
                       Case Control_Status          ' controllers and switches
                            statusbyte = Bytein
                            Set Ctrl 
                            Set value 
'                       Case ProgChange_Status       ' can be used for different lookup tables
'                            statusbyte = Bytein
'                            Set prog
'                       Case Aftertouch_Status        ' not used on this board
'                            statusbyte = Bytein
'                            Set aft 
'                       Case Pitchbend_Status        
'                            statusbyte = Bytein
'                            Set pblsb 
'                            Set pbmsb
                EndSelect
            Else                                        'midi byte is 7 bits
                Select statusbyte
                       Case 0                           'not a message for this channel
                            GoTo Check_Timers           'disregard
                       Case NoteOff_Status
                            If noteUit = 255 Then
                                noteUit = Bytein
                            Else
                                release = Bytein        'message complete, so we can do the action...
                                Select noteUit
                                       Case 60 To 96  ' dempers
                                            note = Nmap[noteUit]      ' 0 - 25
                                            If note.7 = 0 Then
                                                kanaal = MidiLowDampChannel
                                                If NrLowNotes > 0 Then Dec NrLowNotes
                                            Else
                                                kanaal = MidiHighDampChannel
                                                Clear note.7
                                                If NrHighNotes > 0 Then Dec NrHighNotes
                                            EndIf
                                            If CC64 = 0 Then          ' pedal off
                                                 If release = 0 Then release = CC19       
                                                 HRSOut 128 + kanaal, note, release  
                                            Else
                                                 ' doe niks, geen dempers!
                                            EndIf
                                       Case Lite120_note       ' 120            
                                             Clear Lite120  
                                             Clear Lites.0
                                             Set TimVals[1]
                                             idx = SortTimers ()                                   
                                       Case Lite121_note                   
                                             Clear Lite122
                                             Clear Lites.1
                                             Set TimVals[2]
                                             idx = SortTimers ()
                                       Case Lite122_note         ' can pwm  121
                                             HPWM 2, 0, fPwm       
                                             'Clear Tungsten  
                                       Case Lite123_note            ' 123   
                                             Clear Lite123
                                             Clear Lites.2
                                             Set TimVals[3]
                                             idx = SortTimers ()
                                       Case Display_note        ' 124
                                             Clear Display      
                                       Case Lite127_note         ' can pwm   127      
                                             Clear Lite127
                                             HPWM 1, 0, fPwm                                                                 
                                EndSelect
                                Set noteUit '= 255                         'reset
                            EndIf
                       Case NoteOn_Status
                            If noteAan = 255 Then
                                noteAan = Bytein
                            Else
                                velo = Bytein
                                If velo = 0 Then                      ' note off via velo=0
                                    Select noteAan
                                       Case 60 To 96  ' dempers
                                            note = Nmap[noteAan]      ' 0 - 25
                                            If note.7 = 0 Then
                                                kanaal = MidiLowDampChannel
                                                If NrLowNotes > 0 Then Dec NrLowNotes
                                            Else
                                                kanaal = MidiHighDampChannel
                                                Clear note.7
                                                If NrHighNotes > 0 Then Dec NrHighNotes
                                            EndIf
                                            If CC64 = 0 Then          ' pedal off
                                                 If release = 0 Then release = CC19       
                                                 HRSOut 128 + kanaal, note, release  
                                            Else
                                                 ' doe niks, geen dempers!
                                            EndIf
                                       Case Lite120_note       ' 120            
                                             Clear Lite120 
                                             Clear Lites.0
                                             Set TimVals[1]
                                             idx = SortTimers ()                                   
                                       Case Lite122_note           ' 121        
                                             Clear Lite122
                                             Clear Lites.1
                                             Set TimVals[2]
                                             idx = SortTimers ()
                                       Case Lite121_note         ' can pwm  
                                             HPWM 2, 0, fPwm       
                                             'Clear Tungsten  
                                       Case Lite123_note               
                                             Clear Lite123
                                             Clear Lites.2
                                             Set TimVals[3]
                                             idx = SortTimers ()
                                       Case Display_note         ' 124
                                             Clear Display 
                                       Case Lite127_note         ' can pwm         
                                             Clear Lite127
                                             HPWM 1, 0, fPwm                                                                                                                                                                                                                 
                                    EndSelect
                                Else                                       ' else for if velo = 0
                                    If PowerOn = 1 Then                    ' dont do lights nor send note-on's if power is off
                                    Select noteAan
                                           Case 60 To 108 
                                                note = Nmap[noteAan]
                                                If note.7 = 0 Then
                                                    kanaal = MidiLowNoteChannel
                                                    Inc NrLowNotes
                                                Else
                                                    kanaal = MidiHighNoteChannel
                                                    Clear note.7
                                                    Inc NrHighNotes
                                                EndIf       
                                                HRSOut 144 + kanaal, note, velo
                                           Case Lite120_note       ' 120            
                                                Set Lite120
                                                Set Lites.0 
                                                Pres1 = velo
                                                If velo < 127 Then
                                                    TimVals[1] = time + Dur5[velo]
                                                    idx = SortTimers
                                                EndIf                                    
                                           Case Lite122_note           ' 122        
                                                Set  Lite122
                                                Set Lites.1
                                                Pres2 = velo
                                                If velo < 127 Then
                                                    TimVals[2] = time + Dur5[velo]
                                                    idx = SortTimers ()
                                                EndIf
                                           Case Lite121_note         ' can pwm 
                                                velo = velo + velo 
                                                Inc velo              ' so we can reach 255
                                                HPWM 2,velo , fPwm    'Tungsten  
                                            Case Lite123_note               
                                                Set Lite123
                                                Set Lites.2
                                                Pres3 = velo
                                                If velo < 127 Then
                                                    TimVals[3] = time + Dur5[velo]
                                                    idx = SortTimers ()
                                                EndIf                                               
                                            Case Display_note
                                                Set Display              ' 124
                                            Case Lite127_note            ' can pwm         
                                                velo = velo + velo
                                                Inc velo
                                                HPWM 1, velo, fPwm                                           
                                    EndSelect     
                                    EndIf                                    
                                    Set noteAan                 
                                EndIf  
                            EndIf
                       Case Keypres_Status           
                            If notePres = 255 Then
                                notePres = Bytein       ' = note
                            Else
                                pres = Bytein
                                KeyPres ()     
                                ' changing brightness on the lites required this!
                            EndIf
                       Case Control_Status            'this is where the action takes place for controllers
                            If Ctrl = 255 Then
                                Ctrl = Bytein
                            Else
                                value = Bytein
                                Select Ctrl                      ' this is filtered though.
                                    Case 19
                                         'HRSOut Control_Status, Ctrl, value    - release = damping time here.
                                         'we do not need this controller on the other boards
                                         'If Ctrl = 19 Then CC19 = value + 1
                                         CC19 = value + 1
                                         If CC19 > 127 Then CC19 = 127
                                    Case 64                    ' sustain pedal
                                         CC64 = value
                                         HRSOut 176 + MidiLowDampChannel, 64, value
                                         HRSOut 176 + MidiHighDampChannel, 64, value
                                    Case 66, 123      
                                         HRSOut 176 + MidiLowNoteChannel, 66, value    ' so only relevant controllers are passed to midi out
                                         HRSOut 176 + MidiHighNoteChannel, 66, value 
                                         HRSOut 176 + MidiLowDampChannel, 66, value 
                                         HRSOut 176 + MidiHighDampChannel, 66, value 
                                         Controller ()
                                               ' 66 = power switch        
                                               ' 123 = all notes off
                                EndSelect
                                'Controller ()  ' handles all controllers relevant for this board
                            EndIf
                       Case ProgChange_Status                 'not implemented on <Gesta>
                            If prog = 255 Then                'single byte message
                                 prog = Bytein          
                                 Set prog
                            EndIf
                        Case Aftertouch_Status                'not implemented on <Gesta>
                             If aft = 255 Then
                                  aft = Bytein
                                  Set aft
                             EndIf
                       Case Pitchbend_Status                  'not implemented
                            If pblsb = 255 Then
                                pblsb = Bytein
                            Else
                                pbmsb = Bytein
                                Set pblsb
                            EndIf
                     
                EndSelect
            EndIf

Check_Timers:
If idx < NrTasks Then          ' we moeten alleen checken wanneer er een timer loopt
   If time >= Nxt Then         ' nagaan of de eerstvolgende timer afgelopen is...
        ' in dit geval is de eerste timer afgelopen en moeten we de juiste aktie ondernemen:
        Set Nxt.31                                    ' timer reset, is immers afgelopen
        ' aan de hand van idx weten we welke timer dit is
        Select idx
               Case 0                             ' used for automatic motor timeout
                                                  ' when no notes are playing
'                     Clear MotorOn  '= False: motor stops
'                     Clear MotWait  '= False: timer is afgelopen
                     Set TimVals[0]
'                     Clear PIR2BITS_TMR3IF     ' clear IRQ flag for timer3
'                     Clear T3CON.0
'                     Clear PIE2Bits_TMR3IE
'                     Clear Motor_Relay
'                     Clear BlueLed    
                     idx = SortTimers ()      
               Case 1                             
                     If Lites.0 = 0 Then
                       Clear Lite120
                       Set TimVals[1]
                     Else
                       Toggle Lite120
                       TimVals[1] = time + Dur5[Pres1] 
                     EndIf
                     idx = SortTimers ()
               Case 2
                     If Lites.1 = 0 Then
                       Clear Lite122           
                       Set TimVals[2]
                     Else
                       Toggle Lite122
                       TimVals[2] = time + Dur5[Pres2] 
                     EndIf
                     idx = SortTimers () 
               Case 3
                     If Lites.2 = 0 Then
                       Clear Lite123
                       Set TimVals[3]
                     Else
                       Toggle Lite123
                       TimVals[3] = time + Dur5[Pres3] 
                     EndIf
                     idx = SortTimers ()                        
               'Case Else
               '     ' in dit geval is idx geset
               '     GoTo jumpout               
        EndSelect
   EndIf
      ' beveiliging tegen overflow crashes...
      ' in dit geval loopt er weliswaar een timer!
      If MaxTim = 1 Then
                    Clear time
                  ' Clear notes
                    Set TimVals
      EndIf
Else
      ' idx > LastTask, no timers running, so to avoid overflows, we can reset the loop timer
      ' 26.10.2025: now we switch off power completely (Ubu...):
      ' If MaxTim = 1 Then PowerDown ()   'Clear time 
      If MaxTim = 1 Then Clear time    
EndIf        
              
  Btg PORTB.0     ' average loop-speed: 
                  ' 121kHz with no load. (9.10.2025)
                  ' with autolights in the main loop: 112kHz, 11.10.2025
                  ' with motor control in the main loop: 80kHz
                  ' 22.08.2026, Ekla : 121kHz
Loop
     

Proc SortTimers (),Byte
        'look up the next smallest timer value in the Timvals array
        ' zoek de de volgende kleinste timer waarde:
        Set Result           ' was idx, byte
        Set Nxt.31           ' nxt is set on entry. - for speed, we just set the highest bit
        For i = 0 To LastTask           
            If TimVals[i] < Nxt Then
               Nxt = TimVals[i]         
               Result = i
            EndIf
        Next i
EndProc

'Proc MotorCtrl ()
'    ' cfr.<Ubu> code!!!
'    'called whenever a motorcondition or parameter changes...
'    'this procedure is only called if nrnotes > 0
'    'the nrnotes = 0 case should be handled in  the main loop.
'    If NrNotes > 0 Then
'        If MotWait = True Then   ' in this case a timer is running to put the motor to sleep
'                                 ' and the motor is still running!
'            Clear MotWait
'            Set TimVals[0]       ' we cancel the wait timer, nothing changes for the motor
'            idx = SortTimers ()
'        EndIf
'    EndIf
'        Select CC10                       ' Motspeed set with ctrl.10 ( CC10)
'            Case < 1                      ' stop motor
'                Clear PIR2BITS_TMR3IF     ' clear IRQ flag for timer3
'                Clear T3CON.0
'                Clear PIE2Bits_TMR3IE
'                Clear Motor_Relay
'                Clear BlueLed
'                Clear MotorOn
'                Clear Display
'            Case > 99 '126
'                If NrNotes > 0 Then
'                    Clear PIR2BITS_TMR3IF     ' clear IRQ flag for timer3
'                    Clear T3CON.0
'                    Clear PIE2Bits_TMR3IE
'                    Set Motor_Relay
'                    Set BlueLed
'                    Set MotorOn
'                    Set Display
'                Else
'                    Clear Display
'                EndIf
'            Case Else
'                If NrNotes > 0 Then
'                    If T3CON.0 = 0 Then  
'                        Clear PIR2BITS_TMR3IF     ' clear IRQ flag for timer3
'                        Set Mtog
'                        Clear MotCnt 
'                        MotTim = Ton[CC10]        ' Ton[Motspeed]  
'                        Set T3CON.0
'                        Set PIE2Bits_TMR3IE    ' enable timer3 interrupt
'                        Tim3 = Period          ' 59288 for 5 ms units and period = 500ms.
'                    EndIf
'                    Set MotorOn
'                    Set Display
'                Else
'                    Clear Display
'                EndIf
'         EndSelect                                                   
'EndProc

Proc KeyPres () 
    ' 06.08.2026: To be implemented for <Gesta> !!!
    ' 25.08.2026: done.
    ' 20.10.2026: bug in mapping killed
    Select notePres
        'Case lowest_note To highest_note       
        '    HRSOut Keypres_Status, notePres, pres
        ' could we steer noterepetitions from here?
        Case Lite127_note
            pres = pres + pres 
            Inc pres
            HPWM 1, pres, fPwm
        Case Lite121_note
            pres = pres + pres
            Inc pres
            HPWM 2, pres, fPwm
       ' Case Lite122_note
       '     Pres1 = pres
       ' Case Lite123_note
       '     Pres2 = pres
       ' Case Lite124_note
       '     Pres3 = pres
    EndSelect                
    Set notePres 
EndProc

Proc ProgChange ()
    ' not implemented on this board nor on <Ekla>
    Set prog 
EndProc

Proc Pitchbend ()
    ' used on <Ekla> not on <Gesta>
    Set pblsb 
EndProc

Proc Aftertouch ()
     'this is the channel aftertouch, affecting any playing note
     'not implemented here.
    Set aft                             
EndProc

Proc Controller ()
    Select Ctrl
           Case 19
                ' release controller. 
                release = value + 1
                If release > 127 Then release = 127
                CC19 = release
           Case 66
                'on/off for the robot
                ' this should also go to the note boards!
                If value = 0 Then
                    PowerDown ()
                Else
                    Set Power_Relay      ' stroom AAN via optorelais
                    Set GreenLed 
                    Set PowerOn      ' CC66.0  =1
                    CC19 = CC19_default
                    Set CC69         ' auto lights on
                EndIf
           Case 69 
                CC69 = value
           Case 123
                ' all notes/lites off - no controller resets
            '    If MotorOn = 1 Then
            '    '   start timer to switch motor off
            '        TimVals[0] = time + 500
            '    EndIf
                Clear NrNotes   
                Clear Display     ' lite 124
                Clear Lite120
                Clear Lite122
                HPWM 1, 0, fPwm   ' lite127
                HPWM 2, 0, fPwm   ' lite121
                Clear Lite123  
                Set TimVals[1]
                Set TimVals[2]
                Set TimVals[3]
                idx = SortTimers () 
    EndSelect
    Set Ctrl                                  'mandatory reset
EndProc


Proc PowerDown ()
               Clear Power_Relay      ' power and motor off
               Clear PowerOn          ' CC66.0  =0               
               Set TimVals            ' cancel all timers
               Clear time
               Clear NrNotes          ' both modules
               Clear GreenLed
               Clear Lite120            
               Clear Lite122
               Clear Lite123
               Clear Display
               HPWM 2,0,fPwm          
               HPWM 1,0,fPwm
               CC19 = CC19_default
          '     Clear T3CON.0
          '     Clear PIE2Bits_TMR3IE
               idx = SortTimers ()    ' idx should be 255 here.
EndProc

Proc Dur_Lookup ()
    'lookup for flashing lights:
    Set Dur5[0]         ' not used 
    Dur5[127] =  17218  
    j = Dur5[127] / 127
    For i = 1 To 127
        Dur5[i] = Dur5[0] - (j * i)
    Next i                                      
EndProc  

Proc Note_Map ()
    ' note remapping for <Gesta>
    ' bit 7 is always 1 channel higher
    ' we can use the same lookup for the dampers.
    Set Nmap
    Nmap[60] = 0          ' low module
    Nmap[61] = 1
    Nmap[62] = 128 + 0    ' high module
    Nmap[63] = 128 + 1
    Nmap[64] = 2
    Nmap[65] = 3
    Nmap[66] = 128 + 2
    Nmap[67] = 128 + 3
    Nmap[68] = 4
    Nmap[69] = 5
    Nmap[70] = 128 + 4
    Nmap[71] = 128 + 5
    Nmap[72] = 6
    Nmap[73] = 7
    Nmap[74] = 128 + 6
    Nmap[75] = 128 + 7
    Nmap[76] = 8
    Nmap[77] = 9
    Nmap[78] = 128 + 8
    Nmap[79] = 128 + 9
    Nmap[80] = 10
    Nmap[81] = 11
    Nmap[82] = 128 + 10
    Nmap[83] = 128 + 11 
    Nmap[84] = 12
    Nmap[85] = 13
    Nmap[86] = 128 + 12
    Nmap[87] = 128 + 13
    Nmap[88] = 14
    Nmap[89] = 15
    Nmap[90] = 128 + 14
    Nmap[91] = 128 + 15     
    Nmap[92] = 16
    Nmap[93] = 17
    Nmap[94] = 128 + 16
    Nmap[95] = 128 + 17
    Nmap[96] = 18
    Nmap[97] = 19
    Nmap[98] = 128 + 18
    Nmap[99] = 128 + 19     
    Nmap[100] = 20
    Nmap[101] = 21
    Nmap[102] = 128 + 20
    Nmap[103] = 128 + 21
    Nmap[104] = 22
    Nmap[105] = 23
    Nmap[106] = 128 + 22
    Nmap[107] = 128 + 23
    Nmap[108] = 24
EndProc
'[EOF]

