diff --git a/analog_reader.go b/analog_reader.go index d0d4167..04636d7 100644 --- a/analog_reader.go +++ b/analog_reader.go @@ -6,12 +6,13 @@ import ( ) const ( + // Default number of analog reads to average over. + defaultSamples = 32 + // Calibrated[Min|Max]AI was calculated using the EuroPi calibration program: // https://github.com/Allen-Synthesis/EuroPi/blob/main/software/programming_instructions.md#calibrate-the-module - CalibratedMinAI = 300 - CalibratedMaxAI = 44009 - - DefaultSamples = 1000 + calibratedMinAI = 300 + calibratedMaxAI = 44009 ) func init() { @@ -26,85 +27,80 @@ type AnalogReader interface { Range(steps uint16) uint16 } -// A struct for handling the reading of analogue control voltage. -// The analogue input allows you to 'read' CV from anywhere between 0 and 12V. -type AnalogInput struct { - machine.ADC +type analogInput struct { + adc machine.ADC samples uint16 } -// NewAI creates a new AnalogInput. -func NewAI(pin machine.Pin) *AnalogInput { +func newAnalogInput(pin machine.Pin) *analogInput { adc := machine.ADC{Pin: pin} adc.Configure(machine.ADCConfig{}) - return &AnalogInput{ADC: adc, samples: DefaultSamples} + return &analogInput{adc: adc, samples: defaultSamples} } // Samples sets the number of reads for an more accurate average read. -func (a *AnalogInput) Samples(samples uint16) { +func (a *analogInput) Samples(samples uint16) { a.samples = samples } // Percent return the percentage of the input's current relative range as a float between 0.0 and 1.0. -func (a *AnalogInput) Percent() float32 { - return float32(a.read()) / CalibratedMaxAI +func (a *analogInput) Percent() float32 { + return float32(a.read()) / calibratedMaxAI } // ReadVoltage return the current read voltage between 0.0 and 10.0 volts. -func (a *AnalogInput) ReadVoltage() float32 { +func (a *analogInput) ReadVoltage() float32 { return a.Percent() * MaxVoltage } // Range return a value between 0 and the given steps (not inclusive) based on the range of the analog input. -func (a *AnalogInput) Range(steps uint16) uint16 { +func (a *analogInput) Range(steps uint16) uint16 { return uint16(a.Percent() * float32(steps)) } -func (a *AnalogInput) read() uint16 { +func (a *analogInput) read() uint16 { var sum int for i := 0; i < int(a.samples); i++ { - sum += Clamp(int(a.Get())-CalibratedMinAI, 0, CalibratedMaxAI) + sum += Clamp(int(a.adc.Get())-calibratedMinAI, 0, calibratedMaxAI) } return uint16(sum / int(a.samples)) } -// A struct for handling the reading of knob voltage and position. -type Knob struct { - machine.ADC +type knob struct { + adc machine.ADC samples uint16 } -// NewKnob creates a new Knob struct. -func NewKnob(pin machine.Pin) *Knob { +func newKnob(pin machine.Pin) *knob { adc := machine.ADC{Pin: pin} adc.Configure(machine.ADCConfig{}) - return &Knob{ADC: adc, samples: DefaultSamples} + return &knob{adc: adc, samples: defaultSamples} } // Samples sets the number of reads for an more accurate average read. -func (k *Knob) Samples(samples uint16) { +func (k *knob) Samples(samples uint16) { k.samples = samples } // Percent return the percentage of the knob's current relative range as a float between 0.0 and 1.0. -func (k *Knob) Percent() float32 { +func (k *knob) Percent() float32 { return 1 - float32(k.read())/math.MaxUint16 } // ReadVoltage return the current read voltage between 0.0 and 10.0 volts. -func (k *Knob) ReadVoltage() float32 { +func (k *knob) ReadVoltage() float32 { return k.Percent() * MaxVoltage } // Range return a value between 0 and the given steps (not inclusive) based on the range of the knob's position. -func (k *Knob) Range(steps uint16) uint16 { +func (k *knob) Range(steps uint16) uint16 { return uint16(k.Percent() * float32(steps)) } -func (k *Knob) read() uint16 { +func (k *knob) read() uint16 { var sum int for i := 0; i < int(k.samples); i++ { - sum += int(k.Get()) + sum += int(k.adc.Get()) } return uint16(sum / int(k.samples)) } diff --git a/digital_reader.go b/digital_reader.go index eb8bbe5..3515198 100644 --- a/digital_reader.go +++ b/digital_reader.go @@ -5,7 +5,7 @@ import ( "time" ) -const DefaultDebounceDelay = time.Duration(50 * time.Millisecond) +const defaultDebounceDelay = time.Duration(50 * time.Millisecond) // DigitalReader is an interface for common digital inputs methods. type DigitalReader interface { @@ -15,48 +15,46 @@ type DigitalReader interface { Value() bool } -// DigitalInput is a struct for handling reading of the digital input. -type DigitalInput struct { - Pin machine.Pin +type digitalReader struct { + pin machine.Pin debounceDelay time.Duration lastInput time.Time - callback func(p machine.Pin) + callback func(machine.Pin) } -// NewDI creates a new DigitalInput struct. -func NewDI(pin machine.Pin) *DigitalInput { +func newDigitalReader(pin machine.Pin) *digitalReader { pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) - return &DigitalInput{ - Pin: pin, + return &digitalReader{ + pin: pin, lastInput: time.Now(), - debounceDelay: DefaultDebounceDelay, + debounceDelay: defaultDebounceDelay, } } // LastInput return the time of the last high input (triggered at 0.8v). -func (d *DigitalInput) LastInput() time.Time { +func (d *digitalReader) LastInput() time.Time { return d.lastInput } // Value returns true if the input is high (above 0.8v), else false. -func (d *DigitalInput) Value() bool { +func (d *digitalReader) Value() bool { // Invert signal to match expected behavior. - return !d.Pin.Get() + return !d.pin.Get() } // Handler sets the callback function to be call when a rising edge is detected. -func (d *DigitalInput) Handler(handler func(p machine.Pin)) { - d.HandlerWithDebounce(handler, 0) +func (d *digitalReader) Handler(callback func(machine.Pin)) { + d.HandlerWithDebounce(callback, 0) } // Handler sets the callback function to be call when a rising edge is detected and debounce delay time has elapsed. -func (d *DigitalInput) HandlerWithDebounce(handler func(p machine.Pin), delay time.Duration) { - d.callback = handler +func (d *digitalReader) HandlerWithDebounce(callback func(machine.Pin), delay time.Duration) { + d.callback = callback d.debounceDelay = delay - d.Pin.SetInterrupt(machine.PinFalling, d.debounceWrapper) + d.pin.SetInterrupt(machine.PinFalling, d.debounceWrapper) } -func (d *DigitalInput) debounceWrapper(p machine.Pin) { +func (d *digitalReader) debounceWrapper(p machine.Pin) { t := time.Now() if t.Before(d.lastInput.Add(d.debounceDelay)) { return @@ -65,52 +63,23 @@ func (d *DigitalInput) debounceWrapper(p machine.Pin) { d.lastInput = t } -// Button is a struct for handling push button behavior. -type Button struct { - Pin machine.Pin - debounceDelay time.Duration - lastInput time.Time - callback func(p machine.Pin) +type digitalInput struct { + DigitalReader } -// NewButton creates a new Button struct. -func NewButton(pin machine.Pin) *Button { - pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) - return &Button{ - Pin: pin, - lastInput: time.Now(), - debounceDelay: DefaultDebounceDelay, +func newDigitalInput(pin machine.Pin) *digitalInput { + return &digitalInput{ + newDigitalReader(pin), } } -// Handler sets the callback function to be call when the button is pressed. -func (b *Button) Handler(handler func(p machine.Pin)) { - b.HandlerWithDebounce(handler, 0) +type button struct { + DigitalReader } -// Handler sets the callback function to be call when the button is pressed and debounce delay time has elapsed. -func (b *Button) HandlerWithDebounce(handler func(p machine.Pin), delay time.Duration) { - b.callback = handler - b.debounceDelay = delay - b.Pin.SetInterrupt(machine.PinFalling, b.debounceWrapper) -} - -func (b *Button) debounceWrapper(p machine.Pin) { - t := time.Now() - if t.Before(b.lastInput.Add(b.debounceDelay)) { - return +func newButton(pin machine.Pin) *button { + pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + return &button{ + newDigitalReader(pin), } - b.callback(p) - b.lastInput = t -} - -// LastInput return the time of the last button press. -func (b *Button) LastInput() time.Time { - return b.lastInput -} - -// Value returns true if button is currently pressed, else false. -func (b *Button) Value() bool { - // Invert signal to match expected behavior. - return !b.Pin.Get() } diff --git a/display.go b/display.go index 15fbad8..fb6c126 100644 --- a/display.go +++ b/display.go @@ -17,40 +17,38 @@ const ( ) var ( - DefaultChannel = machine.I2C0 - DefaultFont = &proggy.TinySZ8pt7b - White = color.RGBA{255, 255, 255, 255} + DefaultFont = &proggy.TinySZ8pt7b + White = color.RGBA{255, 255, 255, 255} ) -// Display is a wrapper around `ssd1306.Device` for drawing graphics and text to the OLED. -type Display struct { +type display struct { ssd1306.Device + font *tinyfont.Font } -// NewDisplay returns a new Display struct. -func NewDisplay(channel *machine.I2C, sdaPin, sclPin machine.Pin) *Display { +func newDisplay(channel *machine.I2C, sdaPin, sclPin machine.Pin) *display { channel.Configure(machine.I2CConfig{ Frequency: OLEDFreq, SDA: sdaPin, SCL: sclPin, }) - display := ssd1306.NewI2C(DefaultChannel) - display.Configure(ssd1306.Config{ + d := ssd1306.NewI2C(channel) + d.Configure(ssd1306.Config{ Address: OLEDAddr, Width: OLEDWidth, Height: OLEDHeight, }) - return &Display{Device: display, font: DefaultFont} + return &display{Device: d, font: DefaultFont} } // Font overrides the default font used by `WriteLine`. -func (d *Display) Font(font *tinyfont.Font) { +func (d *display) Font(font *tinyfont.Font) { d.font = font } // WriteLine writes the given text to the display where x, y is the bottom leftmost pixel of the text. -func (d *Display) WriteLine(text string, x, y int16) { +func (d *display) WriteLine(text string, x, y int16) { tinyfont.WriteLine(d, d.font, x, y, text, White) } diff --git a/europi.go b/europi.go index db5a15e..4ac7353 100644 --- a/europi.go +++ b/europi.go @@ -5,53 +5,99 @@ import ( ) const ( + // EuroPi voltage range constants. MaxVoltage = 10.0 MinVoltage = 0.0 + + // EuroPi hardware GPIO pins. + DIPin = machine.GPIO22 + AIPin = machine.ADC0 + + DisplaySdaPin = machine.GPIO0 + DisplaySclPin = machine.GPIO1 + + K1Pin = machine.ADC1 + K2Pin = machine.ADC2 + + B1Pin = machine.GPIO4 + B2Pin = machine.GPIO5 + + CV1Pin = machine.GPIO21 + CV2Pin = machine.GPIO20 + CV3Pin = machine.GPIO16 + CV4Pin = machine.GPIO17 + CV5Pin = machine.GPIO18 + CV6Pin = machine.GPIO19 +) + +var ( + DisplayChannel = machine.I2C0 + CV1PwmGroup = machine.PWM2 + CV2PwmGroup = machine.PWM2 + CV3PwmGroup = machine.PWM0 + CV4PwmGroup = machine.PWM0 + CV5PwmGroup = machine.PWM1 + CV6PwmGroup = machine.PWM1 + + europi *EuroPi ) // EuroPi is the collection of component wrappers used to interact with the module. type EuroPi struct { - // Display is a wrapper around ssd1306.Device - Display *Display + // Display provides methods for drawing to the OLED display. + Display *display - DI DigitalReader - AI AnalogReader + DI *digitalInput + // AI provides methods for reading analog input control voltage between 0 and 12V. + AI *analogInput - B1 DigitalReader - B2 DigitalReader + // B1 is a struct for handling the left push button behavior. + B1 *button + // B2 is a struct for handling the right push button behavior. + B2 *button - K1 AnalogReader - K2 AnalogReader + // K1 provides methods for reading knob voltage and position for the left knob. + K1 *knob + // K2 provides methods for reading knob voltage and position for the left knob. + K2 *knob - CV1 Outputer - CV2 Outputer - CV3 Outputer - CV4 Outputer - CV5 Outputer - CV6 Outputer - CV [6]Outputer + // CV1-6 are structs for interacting with the cv output jacks. + CV1 *output + CV2 *output + CV3 *output + CV4 *output + CV5 *output + CV6 *output + // CV is an array containing all CV outputs. + CV [6]*output } -// New will return a new EuroPi struct. func New() *EuroPi { - cv1 := NewOutput(machine.GPIO21, machine.PWM2) - cv2 := NewOutput(machine.GPIO20, machine.PWM2) - cv3 := NewOutput(machine.GPIO16, machine.PWM0) - cv4 := NewOutput(machine.GPIO17, machine.PWM0) - cv5 := NewOutput(machine.GPIO18, machine.PWM1) - cv6 := NewOutput(machine.GPIO19, machine.PWM1) + if europi == nil { + europi = new() + } + return europi +} + +func new() *EuroPi { + cv1 := newOutput(CV1Pin, CV1PwmGroup) + cv2 := newOutput(CV2Pin, CV2PwmGroup) + cv3 := newOutput(CV3Pin, CV3PwmGroup) + cv4 := newOutput(CV4Pin, CV4PwmGroup) + cv5 := newOutput(CV5Pin, CV5PwmGroup) + cv6 := newOutput(CV6Pin, CV6PwmGroup) return &EuroPi{ - Display: NewDisplay(machine.I2C0, machine.GPIO0, machine.GPIO1), + Display: newDisplay(DisplayChannel, DisplaySdaPin, DisplaySclPin), - DI: NewDI(machine.GPIO22), - AI: NewAI(machine.ADC0), + DI: newDigitalInput(DIPin), + AI: newAnalogInput(AIPin), - B1: NewButton(machine.GPIO4), - B2: NewButton(machine.GPIO5), + B1: newButton(B1Pin), + B2: newButton(B2Pin), - K1: NewKnob(machine.ADC1), - K2: NewKnob(machine.ADC2), + K1: newKnob(K1Pin), + K2: newKnob(K2Pin), CV1: cv1, CV2: cv2, @@ -59,6 +105,6 @@ func New() *EuroPi { CV4: cv4, CV5: cv5, CV6: cv5, - CV: [6]Outputer{cv1, cv2, cv3, cv4, cv5, cv6}, + CV: [6]*output{cv1, cv2, cv3, cv4, cv5, cv6}, } } diff --git a/outputer.go b/outputer.go index 4512bad..501c8a2 100644 --- a/outputer.go +++ b/outputer.go @@ -1,21 +1,22 @@ package europi import ( - "log" "machine" ) const ( // Manually calibrated to best match expected voltages. Additional info: // https://github.com/Allen-Synthesis/EuroPi/blob/main/software/programming_instructions.md#calibrate-the-module - CalibratedOffset = 0 + calibratedOffset = 0 // The default PWM Top of MaxUint16 caused noisy output. Dropping this down to a 8bit value resulted in much smoother cv output. - CalibratedTop = 0xff - CalibratedOffset + calibratedTop = 0xff - calibratedOffset ) -// We need a rather high frequency to achieve a stable cv ouput, which means we need a rather low duty cycle period. -// Set a period of 500ns. -var defaultPeriod uint64 = 500 +var ( + // We need a rather high frequency to achieve a stable cv ouput, which means we need a rather low duty cycle period. + // Set a period of 500ns. + defaultPeriod uint64 = 500 +) // PWMer is an interface for interacting with a machine.pwmGroup type PWMer interface { @@ -28,60 +29,51 @@ type PWMer interface { SetPeriod(period uint64) error } -// Outputer is an interface for interacting with the cv output jacks. -type Outputer interface { - Get() (value uint32) - Voltage(v float32) - On() - Off() -} - -// Outputer is struct for interacting with the cv output jacks. -type Output struct { +// output is struct for interacting with the cv output jacks. +type output struct { pwm PWMer pin machine.Pin ch uint8 } -// NewOutput returns a new Output struct. -func NewOutput(pin machine.Pin, pwm PWMer) *Output { +func newOutput(pin machine.Pin, pwm PWMer) *output { err := pwm.Configure(machine.PWMConfig{ Period: defaultPeriod, }) if err != nil { - log.Fatal("pwm Configure error: ", err.Error()) + panic("PWM Configure error") } - pwm.SetTop(CalibratedTop) + pwm.SetTop(calibratedTop) ch, err := pwm.Channel(pin) if err != nil { - log.Fatal("pwm Channel error: ", err.Error()) + panic("PWM Channel error") } - return &Output{pwm, pin, ch} + return &output{pwm, pin, ch} } // Get returns the current set voltage in the range of 0 to pwm.Top(). -func (o *Output) Get() uint32 { +func (o *output) Get() uint32 { return o.pwm.Get(o.ch) } // Voltage sets the current output voltage within a range of 0.0 to 10.0. -func (o *Output) Voltage(v float32) { +func (o *output) Voltage(v float32) { v = Clamp(v, MinVoltage, MaxVoltage) invertedCv := (v / MaxVoltage) * float32(o.pwm.Top()) // cv := (float32(o.pwm.Top()) - invertedCv) - CalibratedOffset - cv := float32(invertedCv) - CalibratedOffset + cv := float32(invertedCv) - calibratedOffset o.pwm.Set(o.ch, uint32(cv)) } // On sets the current voltage high at 10.0v. -func (o *Output) On() { +func (o *output) On() { o.pwm.Set(o.ch, o.pwm.Top()) } // Off sets the current voltage low at 0.0v. -func (o *Output) Off() { +func (o *output) Off() { o.pwm.Set(o.ch, 0) } diff --git a/scripts/clockwerk/clockwerk.go b/scripts/clockwerk/clockwerk.go index 217158c..b78aea4 100644 --- a/scripts/clockwerk/clockwerk.go +++ b/scripts/clockwerk/clockwerk.go @@ -49,6 +49,7 @@ var ( // Positive values are multiplications and negative values are divisions. DefaultFactor = [6]int{1, 2, 4, -2, -4, -8} FactorChoices []int + EuroPi = europi.New() ) func init() { @@ -74,8 +75,6 @@ type Clockwerk struct { period time.Duration clocks [6]int resets [6]chan uint8 - - *europi.EuroPi } func (c *Clockwerk) editParams() { @@ -95,7 +94,7 @@ func (c *Clockwerk) editParams() { func (c *Clockwerk) readBPM() uint16 { // Provide a range of 59 - 240 bpm. bpm < 60 will switch to external clock. - _bpm := c.K1.Range((MaxBPM+1)-(MinBPM-2)) + MinBPM - 1 + _bpm := EuroPi.K1.Range((MaxBPM+1)-(MinBPM-2)) + MinBPM - 1 if _bpm < MinBPM { c.external = true _bpm = 0 @@ -110,7 +109,7 @@ func (c *Clockwerk) readBPM() uint16 { } func (c *Clockwerk) readFactor() int { - return FactorChoices[c.K2.Range(uint16(len(FactorChoices)))] + return FactorChoices[EuroPi.K2.Range(uint16(len(FactorChoices)))] } func (c *Clockwerk) startClocks() { @@ -152,11 +151,11 @@ func (c *Clockwerk) clock(i uint8, reset chan uint8) { high, low := c.clockPulseWidth(c.clocks[i]) - c.CV[i].On() + EuroPi.CV[i].On() t = t.Add(high) time.Sleep(t.Sub(time.Now())) - c.CV[i].Off() + EuroPi.CV[i].Off() t = t.Add(low) time.Sleep(t.Sub(time.Now())) } @@ -185,14 +184,14 @@ func (c *Clockwerk) updateDisplay() { return } c.displayShouldUpdate = false - c.Display.ClearBuffer() + EuroPi.Display.ClearBuffer() // Master clock and pulse width. var external string if c.external { external = "^" } - c.Display.WriteLine(external+"BPM: "+strconv.Itoa(int(c.bpm)), 2, 8) + EuroPi.Display.WriteLine(external+"BPM: "+strconv.Itoa(int(c.bpm)), 2, 8) // Display each clock multiplication or division setting. for i, factor := range c.clocks { @@ -203,57 +202,59 @@ func (c *Clockwerk) updateDisplay() { case factor > 1: text = "x" + strconv.Itoa(factor) } - c.Display.WriteLine(text, int16(i*europi.OLEDWidth/len(c.clocks))+2, 26) + EuroPi.Display.WriteLine(text, int16(i*europi.OLEDWidth/len(c.clocks))+2, 26) } xWidth := int16(europi.OLEDWidth / len(c.clocks)) xOffset := int16(c.selected) * xWidth // TODO: replace box with chevron. - tinydraw.Rectangle(c.Display, xOffset, 16, xWidth, 16, europi.White) + tinydraw.Rectangle(EuroPi.Display, xOffset, 16, xWidth, 16, europi.White) - c.Display.Display() + EuroPi.Display.Display() +} + +func (c *Clockwerk) moveSelectedFactor(pin machine.Pin) { + var move int + switch pin { + case europi.B1Pin: + move = -1 + case europi.B2Pin: + move = 1 + } + if EuroPi.B1.Value() && EuroPi.B2.Value() { + c.doClockReset = true + return + } + c.selected = uint8(europi.Clamp(int(c.selected)+move, 0, len(c.clocks)-1)) + c.displayShouldUpdate = true } func main() { c := Clockwerk{ - EuroPi: europi.New(), clocks: DefaultFactor, displayShouldUpdate: true, } // Lower range value can have lower sample size - c.K1.Samples(500) - c.K2.Samples(20) + EuroPi.K1.Samples(500) + EuroPi.K2.Samples(20) - c.DI.Handler(func(pin machine.Pin) { + EuroPi.DI.Handler(func(pin machine.Pin) { // Measure current period between clock pulses. - c.period = time.Now().Sub(c.DI.LastInput()) + c.period = time.Now().Sub(EuroPi.DI.LastInput()) }) // Move clock config option to the left. - c.B1.Handler(func(p machine.Pin) { - if c.B2.Value() { - c.doClockReset = true - return - } - c.selected = uint8(europi.Clamp(int(c.selected)-1, 0, len(c.clocks))) - c.displayShouldUpdate = true - }) + EuroPi.B1.Handler(c.moveSelectedFactor) // Move clock config option to the right. - c.B2.Handler(func(p machine.Pin) { - if c.B1.Value() { - c.doClockReset = true - return - } - c.selected = uint8(europi.Clamp(int(c.selected)+1, 0, len(c.clocks)-1)) - c.displayShouldUpdate = true - }) + EuroPi.B2.Handler(c.moveSelectedFactor) // Init parameter configs based on current knob positions. c.bpm = c.readBPM() c.prevk2 = c.readFactor() c.startClocks() + go europi.DebugMemoryUsedPerSecond() for { // Check for clock updates every 2 seconds. @@ -263,6 +264,6 @@ func main() { c.resetClocks() c.displayShouldUpdate = true } - europi.DebugMemoryUsage() + // europi.DebugMemoryUsage() } } diff --git a/scripts/diagnostics/diagnostics.go b/scripts/diagnostics/diagnostics.go index f6c1043..3112cb0 100644 --- a/scripts/diagnostics/diagnostics.go +++ b/scripts/diagnostics/diagnostics.go @@ -4,76 +4,96 @@ package main import ( "fmt" "machine" + "time" "tinygo.org/x/tinydraw" europi "github.com/awonak/EuroPiGo" ) +var EuroPi = europi.New() + type MyApp struct { knobsDisplayPercent bool + displayShouldUpdate bool prevK1 uint16 prevK2 uint16 staticCv int prevStaticCv int } +func (m *MyApp) updateDisplay() { + if !m.displayShouldUpdate { + return + } + m.displayShouldUpdate = false + + EuroPi.Display.ClearBuffer() + + // Highlight the border of the oled display. + tinydraw.Rectangle(EuroPi.Display, 0, 0, 128, 32, europi.White) + + // Display analog and digital input values. + inputText := fmt.Sprintf("din: %5v ain: %2.2f ", EuroPi.DI.Value(), EuroPi.AI.Percent()) + EuroPi.Display.WriteLine(inputText, 3, 8) + + // Display knob values based on app stateuropi. + var knobText string + if m.knobsDisplayPercent { + knobText = fmt.Sprintf("K1: %0.2f K2: %0.2f", EuroPi.K1.Percent(), EuroPi.K2.Percent()) + } else { + knobText = fmt.Sprintf("K1: %2d K2: %2d", EuroPi.K1.Range(100), EuroPi.K2.Range(100)) + } + EuroPi.Display.WriteLine(knobText, 3, 18) + + // Show current button press stateuropi. + EuroPi.Display.WriteLine(fmt.Sprintf("B1: %5v B2: %5v", EuroPi.B1.Value(), EuroPi.B2.Value()), 3, 28) + + EuroPi.Display.Display() +} func main() { myApp := MyApp{ staticCv: 5, } - e := europi.New() - // Demonstrate adding a IRQ handler to B1 and B2. - e.B1.Handler(func(p machine.Pin) { + EuroPi.B1.Handler(func(p machine.Pin) { myApp.knobsDisplayPercent = !myApp.knobsDisplayPercent + myApp.displayShouldUpdate = true }) - e.B2.Handler(func(p machine.Pin) { + EuroPi.B2.Handler(func(p machine.Pin) { myApp.staticCv = (myApp.staticCv + 1) % europi.MaxVoltage - }) - - for { - e.Display.ClearBuffer() - - // Highlight the border of the oled display. - tinydraw.Rectangle(e.Display, 0, 0, 128, 32, europi.White) + myApp.displayShouldUpdate = true - // Display analog and digital input values. - inputText := fmt.Sprintf("din: %5v ain: %2.2f ", e.DI.Value(), e.AI.Percent()) - e.Display.WriteLine(inputText, 3, 8) - - // Display knob values based on app state. - var knobText string - if myApp.knobsDisplayPercent { - knobText = fmt.Sprintf("K1: %0.2f K2: %0.2f", e.K1.Percent(), e.K2.Percent()) - } else { - knobText = fmt.Sprintf("K1: %2d K2: %2d", e.K1.Range(100), e.K2.Range(100)) - } - e.Display.WriteLine(knobText, 3, 18) + }) - // Show current button press state. - e.Display.WriteLine(fmt.Sprintf("B1: %5v B2: %5v", e.B1.Value(), e.B2.Value()), 3, 28) + go europi.DebugMemoryUsedPerSecond() - e.Display.Display() + for { // Set voltage values for the 6 CV outputs. - if e.K1.Range(1<<12) != myApp.prevK1 { - e.CV1.Voltage(e.K1.ReadVoltage()) - e.CV4.Voltage(europi.MaxVoltage - e.K1.ReadVoltage()) - myApp.prevK1 = e.K1.Range(1 << 12) + if EuroPi.K1.Range(1<<12) != myApp.prevK1 { + EuroPi.CV1.Voltage(EuroPi.K1.ReadVoltage()) + EuroPi.CV4.Voltage(europi.MaxVoltage - EuroPi.K1.ReadVoltage()) + myApp.prevK1 = EuroPi.K1.Range(1 << 12) + myApp.displayShouldUpdate = true } - if e.K2.Range(1<<12) != myApp.prevK2 { - e.CV2.Voltage(e.K2.ReadVoltage()) - e.CV5.Voltage(europi.MaxVoltage - e.K2.ReadVoltage()) - myApp.prevK2 = e.K2.Range(1 << 12) + if EuroPi.K2.Range(1<<12) != myApp.prevK2 { + EuroPi.CV2.Voltage(EuroPi.K2.ReadVoltage()) + EuroPi.CV5.Voltage(europi.MaxVoltage - EuroPi.K2.ReadVoltage()) + myApp.prevK2 = EuroPi.K2.Range(1 << 12) + myApp.displayShouldUpdate = true } - e.CV3.On() + EuroPi.CV3.On() if myApp.staticCv != myApp.prevStaticCv { - e.CV6.Voltage(float32(myApp.staticCv)) + EuroPi.CV6.Voltage(float32(myApp.staticCv)) myApp.prevStaticCv = myApp.staticCv + myApp.displayShouldUpdate = true } + + myApp.updateDisplay() + time.Sleep(10 * time.Millisecond) } }