// Beispiele zu dem Buch
// Richard Kaiser: Raspberry Pi Pico
// Mikrocontroller Programmierung
// ??? das funktioniert inzwischen nicht mehr:
// In diagram.json these lines
// [ "pico:GP0", "$serialMonitor:RX", "", [] ],
// [ "pico:GP1", "$serialMonitor:TX", "", [] ],
// were removed, so that
// Serial instead of Serial1
// can be used. (RK)
#define RUN_ON_WOKWI // define for running on Wokwi, and not an a real board
// configuration macros so that cpp-excercises.h,
// utils.h etc work for different boards (STM32 C031C6, ESP32
// and Raspberry Pico), gcc compiler versions and IDEs
#define RASPI_PICO // so that utils.h works for all boards
#define RASPI RASPI_PICO_2 | RASPI_PICO_W | RASPI_PICO
#define WOKWI_USED
// ----------------------------------------------------
// ??? D:\cpp-2015.boo\Beispiele\Embedded-Cpp\MC_Examples.h
// The macros RASPI, ESP32, STM32_NUCLEO identify the board
// used. They are defined in the ino file of the project.
// ??? #define RUN_ON_WOKWI // im Wokwi-Projekt definieren /// ???? das war #define WOKWI_USED
#ifndef SMALL_MEMORY // #define SMALL_MEMORY for boards with little memory (e.g. < 300KB)
# include <iostream>
#endif
#include <array>
#ifdef RUN_ON_WOKWI
const float pi = PI;
namespace N_MC_Utils
{
// '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
void show_gcc_version()
{ // In a Wokwi project for a Raspberry Pi Pico, you eventually have
// to use 'Serial1' instead of 'Serial' (see section 3.2.1.2)
Serial.println("Examples: Various ways to print the GCC Version ");
Serial.print("1. Serial.println GCC Version: ");
Serial.print(__GNUC__); Serial.print(".");
Serial.print(__GNUC_MINOR__); Serial.print(".");
Serial.println(__GNUC_PATCHLEVEL__);
// the same with printf
printf("2. Printf GCC Version: %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#ifndef SMALL_MEMORY // A board with not enough memory for #include <iostream>
std::cout << "3. std::cout GCC Version: " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__ << std::endl;
#endif
std::string vs = "std::string GCC Version: " +
std::to_string(__GNUC__) +
"." + std::to_string(__GNUC_MINOR__) +
"." + std::to_string(__GNUC_PATCHLEVEL__) +
"; cplusplus=" + std::to_string(__cplusplus);
printf("%s\n", ("4. printf " + vs).c_str());
Serial.println(("5. Serial.println " + vs).c_str());
// 5. Serial.println accepts char* arguments, but not std::string
// arguments
#ifndef PRINTF_DOES_NOT_WORK
Serial.printf("6. Serial.printf %s\n", vs.c_str());
#endif
}
void setup_serial_port() // ??? ..\Beispiele\Embedded-Cpp\Wokwi-Libraries\utils.h -- 3.2
{
Serial.begin(115200); // Initialize the serial port. Without
// Serial.begin, you cannot send messages to the serial console.
// Baud rate: The number of bits transmitted per
// second. Typical values are e.g. 9600 or 19200.
// High values like 115200 are ok for most boards
while (!Serial) // Wait for the serial port to connect.
; // This is only necessary for older boards.
delay(100); // For some boards you have to wait a little more
Serial.println("Mikrocontroller Programmierung in C++ (www.rkaiser.de) - serial port ready");
show_gcc_version();
}
namespace N_Pins // Pin numbers for the Wokwi circuits from section
{ // Use the onboard-LED symbols if they are defined.
#ifdef RASPI // macro for the Raspberry project
# ifdef LED_BUILTIN // for Pico and Pico 2
constexpr uint32_t pin_onboard_LED = LED_BUILTIN; // 25u
# endif
constexpr uint32_t pin_onboard_PushButton = 25; // ???
#elif defined ESP32 // macro defined in *.ino for the ESP32 project
// Some ESP32 boards have a builtin LED at pin 2, some not.
// If not, connect a LED to pin 2.
// The ESP32 has no symbolic name for the builtin LED.
constexpr uint8_t pin_onboard_LED = 2;
constexpr uint8_t pin_blinking_LED = pin_onboard_LED;
// Most ESP32 boards have no builtin push button.
// Choose a GPIO pin number an connect a pushButton to it
constexpr uint8_t pin_onboard_PushButton = 3; // or some other pin
enum class DAC_pin {
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/dac.html
DAC1 = DAC1, // 25
DAC2 = DAC2 // 26
};
constexpr uint32_t to_int(DAC_pin pin)
{
return static_cast<uint32_t>(pin);
}
#elif defined STM32_NUCLEO // macro for the STM32 project
# ifdef LED_BUILTIN // for all STM32 nucleo boards
constexpr uint32_t pin_onboard_LED = LED_BUILTIN; // PIN13, usually PA5
# endif
# ifdef BUTTON_BUILTIN // for all STM32 nucleo boards
constexpr uint32_t pin_onboard_PushButton = BUTTON_BUILTIN; // PIN_USER, usually PC13
# endif
#else
static_assert(false, "You have to define a macro for the board you are working with in the ino file of your project, e.g. #define MY_BOARD");
#error You have to define a macro for the board you are working with in the ino file of your project, e.g. #define MY_BOARD
#endif
// Global variables should always be const (C++ core guidelines
// I.2, R.6). Prefer variables over macros. Some syntactic variants
constexpr uint32_t pin_red_light = 2; // traditional syntax with = ??? nur red_light
constexpr uint32_t pin_yellow_light = { 4 };//initializer list with =
constexpr uint32_t pin_green_light{ 5 }; // initializer list with {}
constexpr uint32_t pin_potentiometer = { A0 }; // ??? korrigiert
constexpr uint32_t pin_HC_SR04_trigger = 12;
constexpr uint32_t pin_HC_SR04_echo = 13;
constexpr uint32_t pin_pushButton = 14;
} // namespace N_Pins
uint32_t time_now_in_ms()
{ // a more expressive name than millis
return millis(); // from the Arduino API - 4 Bytes
}
void HandleError(const std::string& msg)
{
printf("Error: %s\n", msg.c_str()); // log a message
// or throw an exception, e.g.
#ifdef __cpp_exceptions
throw std::runtime_error(msg);
#endif // #ifdef __cpp_exceptions
// or set an error flag (not recommended)
// or return std::optional values
};
namespace N_AD_DA_resolutions
{ // AD/DA resolutions for some boards:
enum class Analog_resolution_in_bits_t
{ // Not all values are valid for all boards. Check
// the board documentation for valid values. If you
// need more values, just add them.
// Typical values range from 4 to 16 bits.
bits_8 = 8, // ??? you can change it to a value from 4 to 16 bits
bits_10 = 10, bits_12 = 12, bits_16 = 16
};
constexpr uint32_t to_int(Analog_resolution_in_bits_t bits)
{
return { static_cast <uint32_t>(1 << static_cast<uint32_t>(bits)) - 1 };
}
void to_int_compiletime_unittests()// some unit tests
{
static_assert(to_int(Analog_resolution_in_bits_t::bits_10) == 1023, "to_int(bits_10) == 1023");
static_assert(to_int(Analog_resolution_in_bits_t::bits_12) == 4095, "to_int(bits_12) == 4095");
// Fehler: static_assert(to_int(Analog_resolution_in_bits_t::bits_12) == 4096, "to_int(bits_12) == 4095");
}
#ifdef RASPI
constexpr auto default_AnalogRead_resolution_in_bits = Analog_resolution_in_bits_t::bits_10;
constexpr auto default_AnalogWrite_resolution_in_bits = Analog_resolution_in_bits_t::bits_8;
#endif // #ifdef RASPI
#ifdef ESP32
constexpr auto default_AnalogRead_resolution_in_bits = Analog_resolution_in_bits_t::bits_10;
constexpr auto default_AnalogWrite_resolution_in_bits = Analog_resolution_in_bits_t::bits_8;
#endif // #ifdef ESP32
#ifdef STM32_NUCLEO
constexpr auto default_AnalogRead_resolution_in_bits = Analog_resolution_in_bits_t::bits_12;
constexpr auto default_AnalogWrite_resolution_in_bits = Analog_resolution_in_bits_t::bits_8;
#endif // #ifdef STM32_C031C6
#ifndef RUN_ON_WOKWI
constexpr auto default_AnalogRead_resolution_in_bits = N_Board::default_AnalogRead_resolution_in_bits;
constexpr auto default_AnalogWrite_resolution_in_bits = N_Board::default_AnalogWrite_resolution_in_bits;
#endif
constexpr auto default_AnalogRead_resolution_in_ints = to_int(default_AnalogRead_resolution_in_bits);
constexpr auto default_AnalogWrite_resolution_in_ints = to_int(default_AnalogWrite_resolution_in_bits);
} // namespace N_AD_DA_resolutions
namespace N_AD_DA_map
{
float map_AD_linear(uint32_t x, uint32_t in_min, uint32_t in_max, float out_min, float out_max)
{ // x is an int value read from an AD-Converter by analogRead. The
// result is a float value in the range out_min ... out_max
return { (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min };
// map_AD...(in_min, ...) = out_min, map_AD...(in_max, ...) = out_max
// x€[in_min,in_max] --> [out_min,out_max]
// x=in_min: return out_min
// x=in_max: return out_max
}
uint32_t map_DA_linear(float x, float in_min, float in_max, uint32_t out_min, uint32_t out_max)
{ // x is a float value in the range in_min .. out_min
// result is an int value in the range out_min .. out_max
// that will be written by a DA-converter using analogWrite
float f{ (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min };
const float round = 0.5;
return { static_cast<uint32_t>(f + round) }; // adding 0.5 rounds to int
// map_DA...(in_min, ...) = out_min, map_DA...(in_max, ...) = out_max
// x€[in_min,in_max] --> [out_min,out_max]
// x=in_min: return out_min
// x=in_max: return out_max
}
} // namespace N_AD_DA_map
} // namespace N_MC_Utils
#endif // #ifdef RUN_ON_WOKWI
namespace N_MC_Examples
{
using N_MC_Utils::setup_serial_port;
namespace N_3_2_1_introductory_example
{
void setup() // ??? ...\Beispiele\Embedded-Cpp\Einfuehrung_MC_Beispiele.h
{
setup_serial_port();
}
void loop() // ??? ...\Beispiele\Embedded-Cpp\Einfuehrung_MC_Beispiele.h
{
Serial.println("Every second a greeting from the loop ");
Serial.print(millis());
Serial.println(" milliseconds since program start ");
delay(1000); // wait 1000 milliseconds: busy waiting -
// no other statement is executed before time is over
}
} // namespace N_3_2_1_introductory_example
namespace N_3_3_2_blink_LED
{
#ifdef LED_BUILTIN
auto pin_blinking_LED = N_MC_Utils::N_Pins::pin_onboard_LED;
#else
auto pin_blinking_LED = N_MC_Utils::N_Pins::pin_red_light;
#endif
void setup()
{
setup_serial_port(); // the usual stuff
pinMode(pin_blinking_LED, OUTPUT);
Serial.print("Pin ");
Serial.print(pin_blinking_LED);
Serial.println(" ready for output to blink");
}
void loop() // blinking LED. Important: Do not forget the 220 Ohm
{ // resistor, otherwise the LED will be destroyed
digitalWrite(pin_blinking_LED, HIGH); // turns the LED on
delay(1000); // wait for 1000 milliseconds
digitalWrite(pin_blinking_LED, LOW); // turns the LED off
delay(1000); // wait for 1000 milliseconds
} // siehe auch https://www.arduino.cc/reference/en/language/functions/digital-io/digitalwrite/
}
namespace N_3_3_3_Simple_Trafficlights
{
const int pin_red_LED = N_MC_Utils::N_Pins::pin_red_light; // 2
const int pin_yellow_LED = N_MC_Utils::N_Pins::pin_yellow_light; // 4;
const int pin_green_LED = N_MC_Utils::N_Pins::pin_green_light; // 5;
void setup_trafficlight_pins()
{
pinMode(pin_red_LED, OUTPUT); // configure pin 2 as output
pinMode(pin_yellow_LED, OUTPUT); // configure pin 4 as output
pinMode(pin_green_LED, OUTPUT); // configure pin 5 as output
Serial.println("setup simple traffic lights ");
}
void setup()
{
setup_serial_port();
setup_trafficlight_pins();
}
const int turn_on = HIGH;
const int turn_off = LOW;
const int duration_red_phase = 7000;
const int duration_red_yellow_phase = 1000;
const int duration_green_phase = 5000;
const int duration_yellow_phase = 1000;
void loop()
{
digitalWrite(pin_red_LED, turn_on); // voltage to pin 2, connected to the red LED, turned on
delay(7000); // the program waits until 7 seconds are over
digitalWrite(pin_yellow_LED, turn_on); // turn the yellow LED on,
delay(1000); // wait 1 second, red is still turned on
digitalWrite(pin_red_LED, turn_off); // set voltage to 0 Volt at pin 2, turns red LED off
digitalWrite(pin_yellow_LED, turn_off); // turns the yellow LED off
digitalWrite(pin_green_LED, turn_on); // turns the green LED on
delay(5000); // green LED lights for 5 seconds
digitalWrite(pin_green_LED, turn_off); // turns the green LED off
digitalWrite(pin_yellow_LED, turn_on); // turns the yellow LED on
delay(1000); // yellow LED remains on for 1 second
digitalWrite(pin_yellow_LED, turn_off); // turns the yellow LED off
}
}
namespace N_3_3_4_FSM_Trafficlights // finite state machine with int states
{
using namespace N_3_3_3_Simple_Trafficlights; /* use the symbols
const int pin_red_LED = 2;
const int pin_yellow_LED = 4;
const int pin_green_LED = 5;
const int turn_on = HIGH;
const int turn_off = LOW;
from the last example */
void switch_to_red_phase()
{
digitalWrite(pin_red_LED, turn_on);
digitalWrite(pin_yellow_LED, turn_off);
digitalWrite(pin_green_LED, turn_off);
}
void switch_to_red_yellow_phase()
{
digitalWrite(pin_red_LED, turn_on);
digitalWrite(pin_yellow_LED, turn_on);
digitalWrite(pin_green_LED, turn_off);
}
void switch_to_green_phase()
{
digitalWrite(pin_red_LED, turn_off);
digitalWrite(pin_yellow_LED, turn_off);
digitalWrite(pin_green_LED, turn_on);
}
void switch_to_yellow_phase()
{
digitalWrite(pin_red_LED, turn_off);
digitalWrite(pin_yellow_LED, turn_on);
digitalWrite(pin_green_LED, turn_off);
}
void setup_with_phases()
{
using N_3_3_3_Simple_Trafficlights::setup_trafficlight_pins; // ??? raus
setup_serial_port(); // see section 3.3
setup_trafficlight_pins();
Serial.println("traffic lights Version 2: loop_with_phases");
}
const int red_phase = 1; // int values for the phaes
const int red_yellow_phase = 2;
const int green_phase = 3;
const int yellow_phase = 4;
int current_phase = red_phase;
int next_Phase = red_phase; // start with red
// ??? This is not needed for busy waiting, since the current_phase
// is adjusted at the end of each phase. Without busy waiting,
// the current_phase may still be executed while the next_phase
// is already determined.
uint64_t starttime_currentphase = N_MC_Utils::time_now_in_ms();
uint64_t duration_currentphase = 0;
void setup_FSM_without_busy_waiting()
{
using N_3_3_3_Simple_Trafficlights::setup_trafficlight_pins;
setup_serial_port();
setup_trafficlight_pins();
Serial.println("traffic lights Version 3: loop_FSM_without_busy_waiting");
}
void loop_FSM_without_busy_waiting()
{
using N_MC_Utils::time_now_in_ms;
if (time_now_in_ms() > starttime_currentphase + duration_currentphase)
{
current_phase = next_Phase;
if (current_phase == red_phase)
{
switch_to_red_phase();
duration_currentphase = duration_red_phase;
next_Phase = red_yellow_phase;
starttime_currentphase = time_now_in_ms(); // oder nur einmal nach if ... else if ...
}
else if (current_phase == red_yellow_phase)
{
switch_to_red_yellow_phase();
duration_currentphase = duration_red_phase;
next_Phase = green_phase;
// starttime_currentphase = time_now_in_ms(); // oder nur einmal nach if ... else if ...
}
else if (current_phase == green_phase)
{
switch_to_green_phase();
duration_currentphase = duration_green_phase;
next_Phase = yellow_phase;
// starttime_currentphase = time_now_in_ms(); // oder nur einmal nach if ... else if ...
}
else if (current_phase == yellow_phase)
{
switch_to_yellow_phase();
duration_currentphase = duration_yellow_phase;
next_Phase = red_phase;
// starttime_currentphase = time_now_in_ms(); // oder nur einmal nach if ... else if ...
}
else // ???
{
N_MC_Utils::HandleError("Fehler: unzulässiger Wert von Ampelphase");
}
starttime_currentphase = N_MC_Utils::time_now_in_ms(); // Arduino-API: delay
}
printf("aktuelle_Zeit: %d\n", N_MC_Utils::time_now_in_ms());
}
void benchmark_loop_FSM_without_busy_waiting() // ???
{
using N_MC_Utils::time_now_in_ms;
int t_start = time_now_in_ms();
loop_FSM_without_busy_waiting();
int t_end = time_now_in_ms();
std::string t_str = std::to_string(t_end - t_start);
Serial.println(t_str.c_str());
}
}
namespace N_3_3_5_Digital_Input_with_PushButton
{
const uint32_t pin_pushButton = N_MC_Utils::N_Pins::pin_pushButton; // 14 bei den wokwi-Schaltungen
const uint32_t pin_LED = N_MC_Utils::N_Pins::pin_red_light; // 2 bei den wokwi-Schaltungen
void pin_ISR()
{
volatile auto statePushButton = digitalRead(pin_pushButton);
if (statePushButton == HIGH)
{
digitalWrite(pin_LED, HIGH); // turn LED on:
}
else
{
digitalWrite(pin_LED, LOW); // turn LED off
}
}
const bool With_Interrupts = true;
void setup()
{
setup_serial_port(); // see section ??? 4.2
pinMode(pin_pushButton, INPUT);
pinMode(pin_LED, OUTPUT);
if (With_Interrupts)
attachInterrupt(pin_pushButton, pin_ISR, CHANGE);
Serial.print("Input-pin="); Serial.print(pin_pushButton);
Serial.print(" output-pin="); Serial.print(pin_LED);
Serial.println(" ready ");
}
void loop() // check if the pushButton connected
{ // to pin_pushButton is pressed
int statePushButton = digitalRead(pin_pushButton);
if (statePushButton == HIGH)
{
digitalWrite(pin_LED, HIGH); // turn LED on:
}
else
{
digitalWrite(pin_LED, LOW); // turn LED off
}
delay(100);
}
}
namespace N_3_3_6_PushButton_with_Interrupt
{
const auto pin_pushButton = N_MC_Utils::N_Pins::pin_pushButton; // 14 bei den wokwi-Schaltungen
const auto pin_LED = N_MC_Utils::N_Pins::pin_red_light; // 2 bei den wokwi-Schaltungen
volatile int InterruptCounter = 0;
void ISR_PushButton()
{
InterruptCounter++;
volatile auto statePushButton = digitalRead(pin_pushButton);
if (statePushButton == HIGH)
{
digitalWrite(pin_LED, HIGH); // turn LED on:
}
else
{
digitalWrite(pin_LED, LOW); // turn LED off
}
}
void setup()
{
N_3_3_5_Digital_Input_with_PushButton::setup(); // as in the last example
attachInterrupt(pin_pushButton, ISR_PushButton, CHANGE); // ???
}
// After attaching the ISR with
// attachInterrupt(pin_pushButton, pin_ISR, CHANGE);
// in setup, the ISR is called when the pushButton
// is pressed or released.
// Although the Counter is not modified in loop, it changes.
void loop() // An empty loop is sufficient.
{
Serial.print("InterruptCounter=");
Serial.println(InterruptCounter);
delay(5000); // wait long enough so that you can push the button several times
}
} // namespace N_3_3_6_PushButton_with_Interrupt
namespace N_3_4_1_AnalogRead
{
void setup()
{
setup_serial_port();
// For most boards (e.g. Raspberry Pi Pico, ESP32, or STM32
// Nucleo) it is not necessary to configure analog input pins
// before calling analogRead because they are ready to use by
// default in the Arduino API.
}
const int pin_potentiometer = N_MC_Utils::N_Pins::pin_potentiometer; // A0
const int analogRead_resolution = N_MC_Utils::N_AD_DA_resolutions::default_AnalogRead_resolution_in_ints;
namespace N_Servo // ??? wieder raus???
{
/*
Servosteuerung ohne Library
*/
const int potiPin = A0;
const int servoPin = 9;
// linker und rechter Anschlag in Grad
const int minValue = 0;
const int maxValue = 180;
void setup()
{
pinMode(servoPin, OUTPUT);
}
int getPotiValue() {
return map(analogRead(potiPin), 0, 1023, minValue, maxValue);
}
void setServo(int pos)
{
int puls = map(pos, minValue, maxValue, 400, 2400);
digitalWrite(servoPin, HIGH);
delayMicroseconds(puls);
digitalWrite(servoPin, LOW);
delay(19);
}
void loop()
{
setServo(getPotiValue());
}
}
int mock_analogRead = 0;
void loop()
{
using N_MC_Utils::N_AD_DA_map::map_AD_linear;
int raw_value = analogRead(pin_potentiometer); // read from A0
#ifdef RUN_ON_WOKWI
if (bool test = false)
#else
if (bool test = true)
#endif
{
raw_value = mock_analogRead;
mock_analogRead = mock_analogRead + 100;
if (mock_analogRead > analogRead_resolution)
mock_analogRead = 0;
}
// map it to the range of the analog out:
Serial.print(" poti raw value = ");
Serial.print(raw_value);
const uint32_t in_min = 0;
const uint32_t in_max = analogRead_resolution; // 4095 for a real Raspi, ESP32. STM32, 1023 in Wokwi
if (bool Voltmeter = true)
{
// the physical range for a voltage with maximal 3.3 Volts:
// more precise calculation with float values
const float min_physical_value = 0; // physical value for out_min (in physical units, e.g. Volt, temperature, distance, etc.)
const float max_physical_value = 3.3; // physical value for out_max (in physical units, e.g. Volt, temperature, distance, etc.)
float volts = map_AD_linear(raw_value, in_min, in_max, min_physical_value, max_physical_value);
Serial.print(" float volts = ");
Serial.print(volts);
}
if (bool Temperature = true)
{
const float min_physical_value = -20; // physical value for out_min (in physical units, e.g. Volt, temperature, distance, etc.)
const float max_physical_value = 80; // physical value for out_max (in physical units, e.g. Volt, temperature, distance, etc.)
float physical_value = map(raw_value, in_min, in_max, min_physical_value, max_physical_value);
float temperature_in_degrees_celsius = map_AD_linear(raw_value, in_min, in_max, min_physical_value, max_physical_value);
Serial.print(" float temperature = ");
Serial.print(temperature_in_degrees_celsius);
// the physical range for a temperatur sensor with a range from -20 to 80 degrees:
}
Serial.print("\t pin = ");
Serial.println(pin_potentiometer);
// Some boards need a short delay (a few microseconds) between
// successing analogRead calls for the analog-to-digital
// converter to settle
delay(100);
}
} // N_3_4_1_AnalogRead
namespace N_3_6_1_fade_LED
{
using N_MC_Utils::N_Pins::pin_red_light;
using N_MC_Utils::N_AD_DA_resolutions::default_AnalogWrite_resolution_in_ints;
const uint32_t pin_LED{ pin_red_light };
const uint32_t DA_resolution{ default_AnalogWrite_resolution_in_ints }; // 255
void setup()
{
setup_serial_port();
Serial.println("fade the LED at pin_LED in absolute steps");
Serial.print(pin_LED);
Serial.println(" fade the LED at pin_LED in relative steps");
pinMode(pin_LED, OUTPUT);
}
int brightness = DA_resolution; // start with the maximal value
void loop_absolute() // fade the LED at pin_LED in absolute steps
{
brightness = brightness - 10;
if (brightness < 0) // start again with 255
brightness = DA_resolution; // write values in the
analogWrite(pin_LED, brightness); // range 0 .. DA_resolution - 1
printf("duty_cycle=%d pin_LED=%d \n", brightness, pin_LED);
delay(100);
}
float percent = 100;
void loop() // fade the LED at pin_LED in percent
{
using N_MC_Utils::N_AD_DA_map::map_DA_linear;
percent = percent - 10;
if (percent < 0) percent = 100;
int brightness = 0;
#ifdef __cpp_exceptions
try
{
#endif // #ifdef __cpp_exceptions
brightness = map_DA_linear({ percent }, { 0 }, { 100 }, { 0 },
{ DA_resolution });
analogWrite(pin_LED, brightness); // you have to pass a duty cyle
#ifdef __cpp_exceptions
}
catch (std::exception& ex)
{
std::string msg = "percent=" + std::to_string(percent) + ex.what();
printf("%s\n", msg.c_str());
}
#endif // #ifdef __cpp_exceptions
std::string msg = "percent=" + std::to_string(percent) + " duty cycle" + std::to_string(brightness);
printf("%s\n ", msg.c_str());
delay(100);
}
}
namespace N_3_6_2_Audio // ???
{
#ifdef _RAUS_
namespace N_ori // raus ???
{
const int AUDIO_PIN = 15; // GPIO, an dem der Lautsprecher hängt
const int TABLE_SIZE = 100; // Anzahl Samples pro Sinuszyklus
uint8_t sineTable[TABLE_SIZE]; // Tabelle mit 8?Bit Sinuswerten
void setup()
{
pinMode(AUDIO_PIN, OUTPUT);
#ifndef ESP32
analogWriteResolution(8); // 8-Bit PWM (0–255)
analogWriteRange(255);
#endif
// Sinustabelle füllen (0…255)
for (int i = 0; i < TABLE_SIZE; i++)
{
float theta = 2.0 * PI * i / TABLE_SIZE;
sineTable[i] = (uint8_t)((sin(theta) + 1.0f) * 127.5f);
}
}
void playNote(int freq, int duration_ms)
{
int sampleDelay = (1000000 / freq) / TABLE_SIZE; // µs Pause pro Sample
unsigned long endTime = millis() + duration_ms;
int idx = 0;
while (millis() < endTime)
{
analogWrite(AUDIO_PIN, sineTable[idx]); // PWM-Duty gemäß Sinus
idx = (idx + 1) % TABLE_SIZE;
delayMicroseconds(sampleDelay);
}
analogWrite(AUDIO_PIN, 0); // Ton abschalten
}
void loop() {
playNote(440, 500); // A4 für 500?ms
delay(200);
}
}
#endif // #ifdef _RAUS_
const int audioPin = 15; // GPIO pin connected to a speaker
const int numberSamples = 100; // samples for a sine wave
#ifdef __cpp_lib_constexpr_cmath // sin is constexpr as of C++26
constexpr
#endif
std::array<float, numberSamples> fill_sin_lookup_table()
{ // sin table for x=0 to x=2*pi
constexpr auto N = numberSamples; // shortcut
std::array<float, N> sin_table{};
for (int i = 0; i < sin_table.size(); i++)
sin_table[i] = std::sin(2 * pi * i / N); // With float pi, sin(...) is float
return sin_table;
}
std::array<float, numberSamples> sineTable{ fill_sin_lookup_table() };
void setup()
{
pinMode(audioPin, OUTPUT);
for (int i = 0; i < numberSamples; i++)
{ // fill the sine table
float theta = (2.0 * PI * i) / numberSamples;
float y = (sin(theta) + 1.0f) * 127.5f;
sineTable[i] = static_cast<uint8_t>(y);
}
}
void playNote(int freq, int duration_ms)
{
const int sampleDelay = (1000000 / freq) / numberSamples; // µs Pause pro Sample
const unsigned long endTime = millis() + duration_ms;
int idx = 0;
while (millis() < endTime)
{
float theta = (2.0 * PI * idx) / numberSamples;
uint8_t y = static_cast<uint8_t>((sin(theta) + 1.0f) * 127.5f);
analogWrite(audioPin, y); // PWM-Duty sin
idx++;
if (idx >= numberSamples)
idx = 0;
{
analogWrite(audioPin, sineTable[idx]); // PWM-Duty gemäß Sinus
idx++;
if (idx >= numberSamples)
idx = 0;
// or
// i = (i + 1) % numberSamples;
}
delayMicroseconds(sampleDelay);
// wait after each sample, so each sine wave takes (1000000 / freq) µs
}
analogWrite(audioPin, 0); // Ton abschalten
}
void loop() {
playNote(440, 500); // A4 für 500?ms
delay(200);
}
}
namespace N_3_6_Timer_interrupts
{
volatile int InterruptCounter = 0;
const bool CounterExample = true;
#ifdef RASPI
// ??? #include "hardware/timer.h" -use #include "hardware/timer.h" in Pico SDK (CMake-based) projects, not Arduino IDE projects
bool ISR_Timer(struct repeating_timer* t)
{
if (CounterExample)
InterruptCounter++;
else
N_3_3_4_FSM_Trafficlights::loop_FSM_without_busy_waiting();
// digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
return true; // keep repeating
}
void setup()
{
setup_serial_port();
pinMode(LED_BUILTIN, OUTPUT);
static struct repeating_timer timer; // static: timer must outlive the callback function
const int32_t delay_ms = 100; // time between two timer interrupts
add_repeating_timer_ms(delay_ms, ISR_Timer, nullptr, &timer);
}
#elif defined STM32_NUCLEO
HardwareTimer* timer1 = new HardwareTimer(TIM1); // Use TIM1, TIM2, etc. depending on chip
HardwareTimer timer2(TIM2); // Use TIM1, TIM2, etc. depending on chip
void onTimer()
{
if (CounterExample)
InterruptCounter++;
else
N_3_3_4_FSM_Trafficlights::loop_FSM_without_busy_waiting();
// digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void setup()
{
setup_serial_port();
pinMode(LED_BUILTIN, OUTPUT);
if (bool use_timer1 = false)
{
timer1->setOverflow(10, HERTZ_FORMAT); // 1 Hz = 1s interval
timer1->attachInterrupt(onTimer); // Attach the ISR
timer1->resume(); // Start the timer
}
else
{
timer2.setOverflow(1, HERTZ_FORMAT);
timer2.attachInterrupt(onTimer);
timer2.resume();
}
}
#elif defined ESP32
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer_mit_locks()
{ // ESP32 Interrupt-Funktionen müssen mit IRAM_ATTR im RAM abgelegt werden
// portENTER_CRITICAL_ISR(&timerMux);
if (CounterExample)
InterruptCounter++
else
N_3_3_4_FSM_Ampelsteuerung::loop_FSM_without_busy_waiting();
// digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
// portEXIT_CRITICAL_ISR(&timerMux);
}
void loop_mit_locks()
{
if (interruptCounter > 0) // nur nach einem Interrupt (Wert 1)
{ // anzeigen
portENTER_CRITICAL(&timerMux);
interruptCounter--; // wieder auf 0 setzen, damit keine unnötige Anzeige
portEXIT_CRITICAL(&timerMux);
}
}
void IRAM_ATTR onTimer()
{ // ESP32 Interrupt-Funktionen müssen mit IRAM_ATTR im RAM abgelegt werden
if (CounterExample)
InterruptCounter++;
else
N_3_3_4_FSM_Trafficlights::loop_FSM_without_busy_waiting();
// digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
hw_timer_t* timer = nullptr;
void setup()
{
setup_serial_port();
#ifdef ESP_VERSION_2_0_14 // For ESP32, timer interrupts only work with ESP Version 2.0.14
// Full API: timer 0, prescaler 80 (1 µs tick), count up
timer = timerBegin(0, 80, true);
// Bei 80 MHz bewirkt prescaler=80 1.000.000 Ticks pro Sekunde
timerAttachInterrupt(timer, &onTimer, true);
// Trigger every 500,000 µs = 500 ms
timerAlarmWrite(timer, 500000, true);
timerAlarmEnable(timer);
#else
timer = timerBegin(10);
timerAttachInterrupt(timer, &onTimer);
timerStart(timer);
#endif
}
#endif
void loop()
{
if (CounterExample)
{
Serial.print("InterruptCounter=");
Serial.println(InterruptCounter);
}
delay(1000);
}
} // namespace N_3_6_Timer_interrupts
} // namespace N_MC_Examples
using namespace N_MC_Examples;
// Uncommment the namespace declaration
// from which you want setup and loop to execute.
// Uncommment exactly one.
namespace activeNamespace = N_3_2_1_introductory_example;
// namespace activeNamespace = N_3_3_2_blink_LED;
// namespace activeNamespace = N_3_3_3_Simple_Trafficlights;
// namespace activeNamespace = N_3_3_4_FSM_Trafficlights;
// namespace activeNamespace = N_3_3_5_Digital_Input_with_PushButton;
// namespace activeNamespace = N_3_3_6_PushButton_with_Interrupt;
// namespace activeNamespace = N_3_4_1_AnalogRead;
// namespace activeNamespace = N_3_6_1_fade_LED;
// namespace activeNamespace = N_3_6_2_Audio;
// namespace activeNamespace = N_3_6_Timer_interrupts;
void setup()
{
activeNamespace::setup();
}
void loop()
{
activeNamespace::loop();
}