ESP32 Deep Sleep Wakeup Though Touchpad - esp32

I'm trying to write a program that puts my ESP32 into a deep sleep state, and uses touch input to wake it up. I'm able to put it into the deep sleep state, but as soon as it enters, it wakes up and never calls into the callback function.
Reading the raw data from the touch pad, it idles around 25k, and touch inputs from my hand give it a value of around 180k. The 100k value in the code snippet below is the threshold to where I'm comfortable to determine that a touch has been detected.
I'd like to point out that this is different from ext0 and ext1 wake ups.
static void touchsensor_interrupt_cb(void *arg)
{
... // code here turns on an LED and prints to serial
}
void setup(){
...
touch_pad_init();
touch_pad_config(TOUCH_PAD_NUM2);
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
touch_pad_isr_register(touchsensor_interrupt_cb, NULL, TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE);
touch_pad_sleep_channel_enable(TOUCH_PAD_NUM2, true);
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_fsm_start();
esp_sleep_enable_touchpad_wakeup();
Serial.println("entering deep sleep");
esp_deep_sleep_start();
}
I've triple-checked that my circuit is correct. Running on an ESP32S3 Dev Kit v1.0. If there's a better place to post this please let me know.

The issue ended up being this line:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, 100000);
The threshold here was relative to the raw sensor data. Instead, it should be relative to raw_data - benchmark, where (I'm assuming) the benchmark is computed from what it considers the sensor's baseline value. You can get your benchmark using touch_pad_sleep_channel_read_benchmark.
Printing out the expression above on raw, unfiltered data gives values like:
01:25:10.619 -> 3
01:25:10.696 -> -4
01:25:10.774 -> 3
01:25:10.851 -> -10
01:25:10.930 -> -4
01:25:11.006 -> 20
01:25:11.100 -> -2
01:25:11.177 -> 2
01:25:11.255 -> 1
01:25:11.333 -> 0
Where those values oscillate around 0 (the noise in my touch sensor). I changed the line above it to:
touch_pad_sleep_set_threshold(TOUCH_PAD_NUM2, benchmark * threshold);
Where threshold is 0.2, meaning I'll consider a "touch" when the ESP32 reads sensor values of 120% of the benchmark value. I hope this helps someone.

Related

PIC 16F628A clears its registers?

System is basic but I have terrible problem and I can not solve it pls help me. When my system works PIC keep running but clear the registers 4-5 times in a day.
How system should work:
-I have a PIC, pneumatic cylinder and 3 sensor(works with 24V DC).
-Main sensor take the signal from another system.
-When a signal came from main sensor, if the cyclinder is backward, cylinder should go to forward until forward sensor see it and if the cylinder is forward, cyclinder should come to backward until backward sensor see it.
Program:
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or
B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define goForward PIN_A0
#define comeBackward PIN_A1
#define main_sensor PIN_B0
#define positionSensorForward PIN_B5
#define positionSensorBackward PIN_B4
int1 pistonPositionedForward=0, pistonPositionedBackward=1;
int1 positionForwardReg=0, positionBackwardReg=0;
int1 pistonForwarding=0, pistonBackwarding=0;
#priority rb,ext
#int_RB NOCLEAR
void B_change()
{
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg&&pistonForwarding) //if forwarding and forward sensor see
{
disable_interrupts(INT_RB);
output_low(goForward);
pistonPositionedForward=1;
pistonPositionedBackward=0;
write_eeprom(0,1);
write_eeprom(1,0);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
else if(positionBackwardReg&&pistonBackwarding) //if backwarding and backward sensor see
{
disable_interrupts(INT_RB);
output_low(comeBackward);
pistonPositionedForward=0;
pistonPositionedBackward=1;
write_eeprom(0,0);
write_eeprom(1,1);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
clear_interrupt(int_rb);
}
#int_ext NOCLEAR
void ext_interrupt()
{
disable_interrupts(INT_EXT);
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg^positionBackwardReg) //if one of position sensor is see then position according to sensor, else position according to memory
{
pistonPositionedForward=positionForwardReg;
pistonPositionedBackward=positionBackwardReg;
}
if(pistonPositionedForward)
{
pistonBackwarding=1;
pistonForwarding=0;
output_high(comeBackward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
else if(pistonPositionedBackward)
{
pistonForwarding=1;
pistonBackwarding=0;
output_high(goForward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
clear_interrupt(int_ext);
}
void main()
{
//to remember last position after power off
pistonPositionedForward=read_eeprom(0);
pistonPositionedBackward==read_eeprom(1);
set_tris_a(0x00);
set_tris_b(0xFF);
output_a(0x00);
delay_ms(1000);
ext_int_edge(L_TO_H);
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
while(TRUE)
{
}
}
And my circuit:
CIRCUIT
*j2,j3 connected selonoid valve
*J4,J5,J6 connected 3 sensors 1. pin +24VDC,2. pin GND, 3.pin sensor data
***B1 and B2 connections changed. Now B1 connected to B5,B2 connected to B4
And These are I tried:
-I have 3 PIC all of them do same thing
-I changed 24V power supply
-I cancelled 7805 and 7812 and I connected seperate 5V power supply istead of 7805.
I am debugging via LEDs. Sometimes system stop running just waiting at one of positions. Take main sensor signal but doesnot anything, And pistonPositionedForward and pistonPositionedBackward register values are 0. I cant find problem how can it clear these registers?
You have unconnected pins on RB that are configured as inputs, with no internal pull ups set. Electrical noise may well trigger unwanted interrupts on PORTB, that has been known to happen.
The use of interrupts is making the overall logic a bit hard to follow for such a simple device. Have you tried rewriting the program NOT using interrupts (except maybe for EXT)? It should not take long and I think it may greatly improve the reliability - and maintainability, without impacting performance of the physical system.
I suggest you first configure the unused PORTA and PORTB pins as outputs, and see if the problem goes away. If that fails, a rewrite not using interrupts should take no more than an hour. This would probably make sense since that is probably way shorter than the time you have already spent chasing the issue.
Reading the description, I came up with this solution.
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define FWD_MOVE PIN_A0
#define BACK_MOVE PIN_A1
#define PORTA_RESET (0x00) // outputs=LO, unused pins as outputs
#define PORTA_TRISTATE (0x00)
#define EXT_SENSOR PIN_B0
#define FWD_REST PIN_B5
#define BACK_REST PIN_B4
#define PORTB_RESET (0xCE) // can't use pull ups because of HW logic...
#define PORTB_TRISTATE (0x31)
#define EEPROM_STATUS_ADDR (0x0000)
#define EEPROM_STATUS_FWD (0x01)
#define EEPROM_STATUS_BACK (0x00)
int1 extLast;
int1 extCur;
void main()
{
// setup
output_a(PORTA_RESET):
output_b(PORTB_RESET):
// setting to last known state...
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// This will activate the outputs to make sure we have good
// positioning.
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // EEPROM error... I'll let you decide what to do here.
// either move forward or back.
// this implementation goes back by default.
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
// falling through...
case EEPROM_STATUS_BACK:
output_high(BACK_MOVE);
break;
case EEPROM_STATUS_FWD:
output_high(FWD_MOVE);
break;
}
// activate outputs... watch your fingers!
set_tris_a(PORTA_TRISTATE);
set_tris_b(PORTB_TRISTATE);
extLast = input(EXT_SENSOR);
for (;;)
{
// read external sensor, take action.
extCur = input(EXT_SENSOR);
if (extCur && !extlast)
{
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// control logic
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // should never happen.
// falling through...
case EEPROM_STATUS_BACK:
output_high(FWD_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_FWD);
disable_interrupts(GLOBAL);
break;
case EEPROM_STATUS_FWD:
output_high(BACK_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
break;
}
}
extLast = extCur;
// mechanical interface:
// read the limit guards and stop movement when done.
if (input(FWD_REST))
{
output_low(FWD_MOVE);
}
if (input(BACK_REST))
{
output_low(BACK_MOVE);
}
}
}
Of course, I could not check the above code on-site :).
After reviewing the schematics, I must also advise to add 1N4001 diodes in parallel of the 2 output MOSFETS to give them better protection against reverse voltage spikes. MOSFET built-in diodes are not very sturdy. 1N4148 or 1N914 would work there as well.
The 16F628 has very limited stack space. It looks like you are experiencing stack overflow during the call to write_eeprom. Calling write_eeprom from an interrupt may not be such a good idea after all.
There was a bug in older CCS compilers, related to the use of write_eeprom. It seems write_eeprom is enabling interrupts during the call. I've added calls to disable interrupts after the writes. I don't know if that bug was fixed, since I never use CCS.
[EDIT] after checking your HW. I realized you cannot use internal pull-ups because the HW logic is positive-going. The pull-ups in the PIC are meant to work with NPN transistors in the open collector configuration (emitter to ground). I changed the code accordingly.
The way you write to EEPROM is not good. eeprom writes take time, and the second write is usually taken care of in the eeprom interrupt. The CCS bug that enables the global interrupt and unmask the EEIE in eeprom_write does not help. Unhandled interrupts do generate a reset.

Arduino keypad matrix example? ( teensyduino )

I'm a beginner using Arduino with a Teensy 3.2 board and programming it as a usb keyboard.
I have two 4 button membrane switches. Their button contacts are on pins 1-8, and the 9th pin holds a soldered together wire of both membrane switches' "ground" line or whatever it's true name is; the line that completes the circuit.
Basically when you press the buttons they are supposed to simply type "a, b, c..." respectively. I've been told I need to use a matrix for this.
I'm looking for an example of how to code a keyboard matrix that effectively supports a one row/9 column line (or vice versa?) I've been unable to find that solution online.
All I have so far is this code which, when the button on the second pin is pressed, sends tons of "AAAAAAAAAAAAAAAA" keystrokes.
void setup() {
// make pin 2 an input and turn on the
// pullup resistor so it goes high unless
// connected to ground:
pinMode(2, INPUT_PULLUP);
Keyboard.begin();
}
void loop() {
//if the button is pressed
if(digitalRead(2)==LOW){
//Send an ASCII 'A',
Keyboard.write(65);
}
}
Would anyone be able to help?
First of all, a 1-row keypad is NOT a matrix. Or better, technically it can be considered a matrix but... A matrix keypad is something like this:
You see? In order to scan this you have to
Pull Row1 to ground, while leaving rows 2-4 floating
Read the values of Col1-4. These are the values of switches 1-4
Pull Row2 to ground, while leaving rows 1 and 3-4 floating
Read the values of Col1-4. These are the values of switches 5-8
And so on, for all the rows
As for the other problem, you are printing an A when the button is held low. What you want to achieve is to print A only on the falling edge of the pin (ideally once per pressure), so
char currValue = digitalRead(2);
if((currValue==LOW) && (oldValue==HIGH))
{
//Send an ASCII 'A',
Keyboard.write(65);
}
oldValue = currValue;
Of course you need to declare oldValue outside the loop function and initialize it to HIGH in the main.
With this code you won't receive tons of 'A's, but however you will see something like 5-10 'A's every time you press the button. Why? Because of the bouncing of the button. That's what debouncing techniques are for!
I suggest you to look at the class Bounce2 to get an easy to use class for your button. IF you prefer some code, I wrote this small code for another question:
#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5
unsigned long previousMillis;
char stableVals;
char buttonPressed;
...
void loop() {
if ((millis() - previousMillis) > CHECK_EVERY_MS)
{
previousMillis += CHECK_EVERY_MS;
if (digitalRead(2) != buttonPressed)
{
stableVals++;
if (stableVals >= MIN_STABLE_VALS)
{
buttonPressed = !buttonPressed;
stableVals = 0;
if (buttonPressed)
{
//Send an ASCII 'A',
Keyboard.write(65);
}
}
}
else
stableVals = 0;
}
}
In this case there is no need to check for the previous value, since the function already has a point reached only when the state changes.
If you have to use this for more buttons, however, you will have to duplicate the whole code (and also to use more stableVals variables). That's why I suggsted you to use the Bounce2 class (it does something like this but, since it is all wrapped inside a class, you won't need to bother about variables).

SysTick->LOAD vs SysTick->CALIB

I am currently porting my DCF77 library (you may find the source code at GitHub) from Arduino (AVR based) to Arduino Due (ARM Cortex M3).
The library requires precise 1ms timing. An obvious candidate is the use of the systicks. Conveneniently the Arduino Due is already setup for systicks with 1 kHz.
However my (AVR) DCF77 library is capable to tune the timing once it locks to DCF77. This is done by manipulating the timer reload values like so
void isr_handler() {
cumulated_phase_deviation += adjust_pp16m;
// 1 / 250 / 64000 = 1 / 16 000 000
if (cumulated_phase_deviation >= 64000) {
cumulated_phase_deviation -= 64000;
// cumulated drift exceeds 1 timer step (4 microseconds)
// drop one timer step to realign
OCR2A = 248;
} else if (cumulated_phase_deviation <= -64000) {
// cumulated drift exceeds 1 timer step (4 microseconds)
// insert one timer step to realign
cumulated_phase_deviation += 64000;
OCR2A = 250;
} else {
// 249 + 1 == 250 == 250 000 / 1000 = (16 000 000 / 64) / 1000
OCR2A = 249;
}
DCF77_Clock_Controller::process_1_kHz_tick_data(the_input_provider());
}
I want to port this to the ARM processor. In the ARM information center I found the following documentation.
Configuring SysTick
...
To configure the SysTick you need to load the SysTick Reload Value
register with the interval required between SysTick events. The timer
interrupt or COUNTFLAG bit (in the SysTick Control and Status
register) is activated on the transition from 1 to 0, therefore it
activates every n+1 clock ticks. If a period of 100 is required 99
should be written to the SysTick Reload Value register. The SysTick
Reload Value register supports values between 1 and 0x00FFFFFF.
If you want to use the SysTick to generate an event at a timed
interval, for example 1ms, you can use the SysTick Calibration Value
Register to scale your value for the Reload register. The SysTick
Calibration Value Register is a read-only register that contains the
number of pulses for a period of 10ms, in the TENMS field (bits 0 to
23). This register also has a SKEW bit (30) that is used to indicate
that the calibration for 10ms in the TENMS section is not exactly 10ms
due to small variations in clock frequency. Bit 31 is used to indicate
if the reference clock is provided.
...
Unfortunately I did not find anything on how SysTick->LOAD and SysTick->CALIB are connected. That is: if I want to throttle or accelerate systicks, do I need to manipulate the LOAD or the CALIB value? And which values do I need to put into these registers?
Searching the internet did not bring up any better hints. Maybe I am searching at the wrong places.
Is there anywhere a more detailed reference for these questions? Or maybe even some good examples?
Comparing the AtMega328 datasheet with the Cortex-M3 TRM, the standout point is that the timers work opposite ways round: on the AVR, you're loading a value into OCR2A and waiting for the timer in TCNT2 to count up to it, whereas on the M3 you load the delay value into SYST_RVR, then the system will count down from this value to 0 in SYST_CVR.
The big difference for calibration is going to be because the comparison value is fixed at 0 and you can only adjust the reload value, you might have more latency compared to adjusting the comparison value directly (assuming the counter reload happens at the same time the interrupt is generated).
The read-only value in SYST_CALIB (if indeed it even exists, being implementation-defined and optional), is merely for relating SYSTICK ticks to actual wallclock time - when first initialising the timer, you need to know the tick frequency in order to pick an appropriate reload value for your desired period, so having a register field that says "this many reference clock ticks happen in 10ms (possibly)" offers some possibility of calculating that at runtime in a portable fashion, rather than having to hard-code a value that might need changing for different devices.
In this case, however, not only does having an even-more-accurate external clock to synchronise against makes this less important, but crucially, the firmware has already configured the timer for you. Thus you can assume that whatever value is in SYST_RVR represents close-enough-to-1KHz, and work from there - in fact to simply fine-tune the 1KHz period you don't even need to know what the actual value is, just do SysTick->LOAD++ or SysTick->LOAD-- if the error gets too big in either direction.
Delving a bit deeper, the SAM3X datasheet shows that for the particular M3 implementation in that SoC, SYSTICK has a 10.5 MHz reference clock, therefore the SYST_CALIB register should give a value of 105000 ticks for 10ms. Except it doesn't, because apparently Atmel thought it would be really clever to make the unambiguously-named TENMS field give the tick count for 1ms, 10500, instead. Wonderful.
Just for the reason that others do not have to dig around like had to do - here is what I found out in addition.
In arduino-1.5.8/hardware/arduino/sam/system/CMSIS/CMSIS/Include/core_cm*.h there is code to manipulate SysTick. In particular in core_cm3.h there is a function
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
Then in arduino-1.5.8/hardware/arduino/sam/variants/arduino_due_x/variant.cpp in function init there is
// Set Systick to 1ms interval, common to all SAM3 variants
if (SysTick_Config(SystemCoreClock / 1000))
{
// Capture error
while (true);
}
Since SystemCoreClock evaluates to 84000000 it follows that this compiles like SysTick_Config(84000). I verified against a DCF77 module that SysTick_Config(84001) will slow down SysTicks while SysTick_Config(83999) will speed it up.

How do I converge to and fro, between two numbers?

I'm trying to animate a man running:
Frames 1 to 5 = man leaning into run.
Frames 6 to 15 = man running a single step
frame = 1
frame +=1 //frames progress forwards at rate of 1 frame
function run(){
if(frame>15){ //at frame 15: man has completed leaning into run and completed one 'running' cycle
frame -=2 //frames now start to go backwards at rate of (1-2=)-1 frame
if(frame<6){ //until they reach frame 6 (beginning of running animation)
frame +=2 //frames start to progress forwards again at rate of (2-2+1=)+1 frame again
My method is really bad and seems to be only capable of going forwards then backwards ONCE between 15 and 6.
Does anyone know how I can bounce between these two numbers indefinitely?
After reaching frame = 15 and beginning your trip downward, you hit a condition (14) where neither of your IF statements is true. So your frame neither increments nor decrements. Stuck.
A possible better solution would be to maintain a variable called myDirection which toggles periodically between 1 and -1. That is, set myDirection = -1 when you hit 15, and set myDirection = 1 when you hit 6. Then, your iterative statement could always say frame = frame + myDirection and it would always be doing something -- you'd never be stuck doing nothing.
Ok, so using LesterDove's + schnaader's useful tips I've managed:
int step=1
function run(){
frame += step
if(frame>15){ step = -1}
if(frame<6){ step = 1}
}
and it works great guys. Thanks again!

Animating with Ruby

This relates to both physical programming as well as Ruby running on a web server. I have an array of RGB leds, which is 5x5 so a total of 25 leds. They are numbered and individually addressable as such:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
Here is a photo:
As for the hardware (which really isn't important, because it works fine), the system consists of 25 BlinkM's, an Arduino, and some various cabling and connectors.
The led's are sent commands via serial with a command as such:
#sp.write ["\x01", led, "\x04\x00", "c", color]
Which writes the byte array out to serial using ruby's Serialport gem, the variables "led" and "color" are substituted with the hex of each, so for example if I wanted to make led number 8 turn red, my output would read:
#sp.write ["\x01","\x08", "\x04\x00", "c", "\xff\x00\x00"]
So far all of this works wonders, and I'm really happy with what I have, now my question relates pretty much to general mathematics and simple programming, but somehow the implementation goes over my head.
Here is a sample of such animation. Mostly I'm interesting in how one could animate patterns using ruby here. I recall certain "processing" animation scripts, just looping over a function using the array as an object and affecting the elements of the array creating interesting animations just due to the mathematics of the output.
Does anyone have any idea on how I could get started with something like that? I'm currently able to affect the LED's one at a time with my script, and I can string them together with sleep x after each command and manually build animations, but how could I make one run indefinitely with some sort of procedural animation?
EDIT
I really didn't describe the bytecode array in its entirety, here are what each part does:
#sp.write ["\x01", led, "\x04\x00", "c", color]
^ ^ ^ ^ ^ ^
a b c d e f
a. start byte (not important, tells serial that it is the start of a command)
b. hex for LED address, ex. `\x07` is led 7
c. length of command (starting at "e")
d. bytes to be read (always 0 in our case)
e. the "fade to color" command
f. the color we want to fade to in rrggbb hex format.
It should be easy to map your leds to a 2d array
#led = []
led = 1
5.times do |y|
5.times do |x|
#led[x] ||= []
#led[x][y] = led
led +=1
end
end
I'd probably make an LED class that encapsulates the ability to write out colors, so instead of this:
#led[x][y] = led
it becomes
#led[x][y] = Led.new(:id => led)
And then write a method so you can easily do something like this:
#led[1][5].color(255,255,255)
or whatever.
If you just want to make animations, you should try to abstract away the hardware so it can be represented by some data structure that is easy to work with. I'm not familiar with Ruby so I don't know the best way to go about it. If you were making something like that table, which is just a grid, I would try to map the LEDs to a 2D array.
I would then create an infinite loop. This loop would contain another set of loops that iterates through each pixel in that array and writes the color in each element out to the corresponding hardware. Once it writes out all the pixels, it could then sleep for a few ms, call some function that steps your animation when it wakes and repeat the loop again.
Once you do this then all you'll have to manipulate is that data structure. Does that make any sense?
So something like this:
function stepAnimation(){
//modify 2d array for each step of the animation here
}
//I'm assuming you have a function that gets
//Looped forever. In Wiring you do, not sure
//about working with Arduino using Ruby, if not
//just add while(1) in there..
function mainLoop(){
for(var y = 0; y < 5; y++){
for(var x = 0; x < 5; x++){
sp.write(2darray[x][y]) //write color from array to hardware
}
}
sleep(60);
stepAnimation();
}

Resources