For my current project I was hoping at some point to be able to essentially mirror what the Scope displays on an OLED display. For example, below is a simple signal generator project that can generate various waveforms and sends them to the scope for viewing.

#include <Bela.h>
#include <cmath>
#include <libraries/Scope/Scope.h>

float out;

float gFrequency;
float gAmplitude;
int mode;

// Analog inputs
float gPot1;
float gPot2;

// Digital inputs
int gButton = 1;
int gLastButtonStatus = HIGH;
std::vector<int> gNum = {0, 1, 2, 3};  // vector to cylce through that chooses the filter type
unsigned int gNumLocation = 0;      // pointer for vector

Scope scope;

float gPhase;
float gInverseSampleRate;

int gAudioFramesPerAnalogFrame = 0;

// define sign function for square wave from https://helloacm.com/how-to-implement-the-sgn-function-in-c/
int sgn(double v) {
  return ( ( (v) < 0 )  ?  -1   : ( (v) > 0 ) );
}

// signal generation, based on bela's basic.cpp and using definitions from wikipedia
void signal(float gFrequency, float gAmplitude, int mode)
{
	switch(mode){
		case 0:
			out = gAmplitude*sinf(gPhase);           // sine
			break;
		case 1:
			out = gAmplitude*asinf(sinf(gPhase));    // triangle
			break;
		case 2:
			out = gAmplitude*sgn(sinf(gPhase));      // square
			break;
		case 3:
			out = gAmplitude*atanf(tanf(gPhase/2));  // sawtooth
			break;
	}
	gPhase += 2.0 * M_PI * gFrequency * gInverseSampleRate;
		if(gPhase > 2.0 * M_PI)
			gPhase -= 2.0 * M_PI;
}

bool setup(BelaContext *context, void *userData)
{
	// setup oscilloscope with one channel
	scope.setup(1, context->audioSampleRate);
	
	gInverseSampleRate = 1.0 / context->audioSampleRate;
	gPhase = 0.0;
	
	pinMode(context, 0, gButton, INPUT); //set input
	
	// check audio and digital have same no. frames
	if(context->audioFrames != context->digitalFrames) {
		rt_fprintf(stderr, "This example needs audio and digital running at the same rate.\n");
		return false;
	}
	
	// calculate no. audio frames per analog frame
	if(context->analogFrames)
		gAudioFramesPerAnalogFrame = context->audioFrames / context->analogFrames;
		
	// inital signal
	signal(440.0, 0.1, 0);
	
	return true;
}

void render(BelaContext *context, void *userData)
{
	for(unsigned int n = 0; n < context->audioFrames; n++) {
		
		// read analog inputs and update frequency and amplitude
		gPot1 = analogRead(context, n/gAudioFramesPerAnalogFrame, 0);
		gPot2 = analogRead(context, n/gAudioFramesPerAnalogFrame, 1);
		float gFrequency = map(gPot1, 0, 0.8, 100, 1000);
		float gAmplitude = map(gPot2, 0, 0.8, 0.02, 0.5);
			
		// detect falling edge of button and cycle through filter types
		int status = digitalRead(context, n, gButton);
		
		if(status == LOW && gLastButtonStatus == HIGH){
			gNumLocation++;
			if(gNumLocation >= gNum.size()){
				gNumLocation = 0;
			}
		}
		gLastButtonStatus = status;
		mode = gNum[gNumLocation];
	
		signal(gFrequency, gAmplitude, mode);
		
		scope.log(out);

		for(unsigned int channel = 0; channel < context->audioOutChannels; channel++) {
			audioWrite(context, n, channel, out);
		}
	}
}

void cleanup(BelaContext *context, void *userData)
{

}

In this case, I'd want to use the OLED display to show the signal being generated. Would this sort of thing be possible? I'm very new to using an external screen and so far have only actually attempted the example O2O project [https://github.com/giuliomoro/O2O], so a lot of existing discussions regarding OLED screens on this forum go over my head I'm afraid.

We do not provide a straightforward way of taking the Scope's behaviour and pass it on to the O2O program.
However, if you run O2O you can send an OSC message to the /waveform address and append floats that represent the waveform you want to display. For instance (untested):

#include <libraries/OscSender/OscSender.h>

OscSender gOscSender;
size_t gRefreshSamples;
constexpr size_t kColumns = 128; // number of columns on your display
float gDisplayData[kColumns]
size_t gDisplayPtr = 0;
AuxiliaryTask gSendToDisplayTask;

void sendToDisplay(void*) {
    gOscSender.newMessage("/waveform");
    for(size_t n = 0; n < kColumns; ++n) {
        // the value in the buffer was between -1 and 1. Here we make it between 0 and 1
        float value = gDisplayData[n] * 0.5f + 0.5f;
        gOscSender.add(value);
    }
    gOscSender.send();
}

bool setup(BelaContext* context, void*)
{
    ...
   gRefreshSamples = context->audioSampleRate  * 0.05; // 50ms refresh rate
   gOscSender.setup(7562);
   gSendToDisplayTask = Bela_createAuxiliaryTask(sendToDisplay, 1, "sendToDisplay");
    ...
}

float gPastValue = 0;
float gThreshold = 0.2;
void render(BelaContext* context, void*)
{
    for(unsigned int  n = 0; n < context->audioFrames; ++n)
    {
        float value = ... ; /// fill this in
        bool trigger;
        if(gDisplayPtr < kColumns)
            gDisplayData[gDisplayPtr] = value;
       bool trigger = false;
       if(gPastValue < gThreshold && value >= gThreshold)
          trigger = true; // trigger when crossing the threshold with a positive edge
       if(0 == gDisplayPtr && !trigger)
             ; // do nothing
       else 
            gDisplayPtr++;
        if(gDisplayPtr == kColumns) {
            // the buffer is full, schedule the other task, which will send the data to the display
            Bela_scheduleAuxilaryTask(gSendToDisplayTask);
        }
        if(gDisplayPtr == gRefreshSamples)
             gDisplayPtr = 0;

    }
....

}

This will display the temporal evolution of value, capturing kColumns frames every gRefreshSamples frames.

    giuliomoro Just to double check, I need to have O2O running in the background, then run this code (or something similar) as the main project, and value is what is displayed on the screen?

    Okay well now I'm having trouble getting O2O running in the background, it comes up with these errors:

    alt text

    sounds like you have an error in the /lib/systemd/system/O2O-main.service file. Can you paste here the content of the file O2O-main.service in your project? Also I am wondering if you are using a letter O or a number 0 consistently.

    After moving it that directory I'm not sure how to access it again, but I copied the code from this tutorial: https://learn.bela.io/using-bela/bela-techniques/running-a-program-as-a-service/

    [Unit]
    Description=EXEC_NAME Launcher
    After=network-online.target
    
    [Service]
    ExecStart=EXEC_DIR/EXEC_NAME
    Type=simple
    Restart=always
    RestartSec=1
    WorkingDirectory=EXEC_DIR
    Environment=HOME=/root
    KillMode=process
    
    [Install]
    WantedBy=default.target

    Of course. At this page https://learn.bela.io/using-bela/bela-techniques/running-a-program-as-a-service/ you should have found this paragraph:

    The instructions below assume that you already have a working program on your board that you want to run in the background. The executable file will be located somewhere on your Bela’s file system, we use the following placeholder text below, which you will have to replace with the actual values for your system:
    EXEC_DIR is the absolute path to the folder containing your executable file
    EXEC_NAME is the name of the executable file
    For example, if you have a Bela project in your IDE called O2O, its full path will be /root/Bela/projects/O2O/O2O, so EXEC_DIR will be /root/Bela/projects/O2O and EXEC_NAME will be O2O.

    What's your O2O project name? If it's O2O, then the last sentence above should get you going. If it's O2O-main, then EXEC_DIR will be /root/Bela/projects/O2O-main and EXEC_NAME will be O2O-main.

    once you've modified the file in your project, you should copy it over again with the mv command

    Okay now it's running in the background, once again I am my own worst enemy. I've tried including your code above within my project and dealt with as many of the errors as I can, but there's one that I'm not sure how to fix. Within the sendToDisplay function, the line float value = gDisplayPtr[n] * 0.5f + 0.5f; generates subscripted value is not an array, pointer, or vector column: 34, line: 63

      JoelBird unction, the line float value = gDisplayPtr[n] * 0.5f + 0.5f; generates subscripted value is not an array, pointer, or vector column: 34, line: 63

      my fault, should have been gDisplayData[n] .

      Fantastic, that now works very well. Thanks for all your help.