Add Arduino compatibility layer
We can now directly compile arduino sketches (.pde) files. Arduino compatible analogWrite works now. But there is still a long way to go, serial I/O and timer stuff (delay, millis etc) currently don't work (not tested but I don't expect this to work). It can be used in an arduino sketch or in a normal contiki program. We get a PWM frequency of 490.2 Hz (a period of 2.040 ms), that's Arduino compatible. If you need different frequencies see native timer usage in examples/osd/pwm-example In a contiki program you have to call arduino_pwm_timer_init to initialize the timer before pwm works. The arduino sketch wrapper already does this. For running a sketch, see examples/osd/arduino-sketch
This commit is contained in:
parent
87903b2e89
commit
e65dabb119
2
apps/arduino/Makefile.arduino
Normal file
2
apps/arduino/Makefile.arduino
Normal file
|
@ -0,0 +1,2 @@
|
|||
arduino_src = arduino-process.c
|
||||
|
4
apps/arduino/Makefile.include
Normal file
4
apps/arduino/Makefile.include
Normal file
|
@ -0,0 +1,4 @@
|
|||
%.cpp: %.pde
|
||||
echo '#include "Arduino.h"' > $@
|
||||
echo '#include "$<"' >> $@
|
||||
|
13
apps/arduino/README.md
Normal file
13
apps/arduino/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
Arduino Compatibility
|
||||
=====================
|
||||
|
||||
This application contains hardware-independent implementations of
|
||||
arduino compatibilty libraries and include files to be used with
|
||||
contiki.
|
||||
|
||||
The whole arduino compatibility library is work in progress. Note that
|
||||
features having to do with timers like `millis` and `delay` are
|
||||
currently untested. In Arduino they use timer 0 of the AVR
|
||||
microcontroller. It should be investigated to use the hardware timer
|
||||
already in use by contiki (on the OSD-merkur platform this is currently
|
||||
timer 5).
|
77
apps/arduino/arduino-process.c
Normal file
77
apps/arduino/arduino-process.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addgroup Arduino Process
|
||||
*
|
||||
* This wraps the Arduino-API entry points `loop` and `setup` in a
|
||||
* contiki process.
|
||||
*
|
||||
* If the normal contiki includes are used and resources initialized in
|
||||
* `setup`, Contiki resources can be used in an arduino sketch.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Wrapper for Arduino sketches
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "arduino-process.h"
|
||||
|
||||
PROCESS(arduino_sketch, "Arduino Sketch Wrapper");
|
||||
|
||||
PROCESS_THREAD(arduino_sketch, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
|
||||
arduino_init ();
|
||||
setup ();
|
||||
while (1) {
|
||||
loop ();
|
||||
/* Give other processes a chance to run */
|
||||
process_post (&arduino_sketch, PROCESS_EVENT_CONTINUE, NULL);
|
||||
PROCESS_WAIT_EVENT();
|
||||
}
|
||||
PROCESS_END();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* VI settings, see coding style
|
||||
* ex:ts=8:et:sw=2
|
||||
*/
|
||||
|
||||
/** @} */
|
65
apps/arduino/arduino-process.h
Normal file
65
apps/arduino/arduino-process.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \devgroup Arduino Process
|
||||
*
|
||||
* This wraps the Arduino-API entry points `loop` and `setup` in a
|
||||
* contiki process.
|
||||
*
|
||||
* If the normal contiki includes are used and resources initialized in
|
||||
* `setup`, Contiki resources can be used in an arduino sketch.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Wrapper for Arduino sketches
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
|
||||
extern void loop (void);
|
||||
extern void setup (void);
|
||||
extern void arduino_init (void);
|
||||
|
||||
extern struct process arduino_sketch;
|
||||
|
||||
/*
|
||||
* VI settings, see coding style
|
||||
* ex:ts=8:et:sw=2
|
||||
*/
|
||||
|
||||
/** @} */
|
|
@ -137,6 +137,8 @@
|
|||
#define HWT_CHANNEL_A 0
|
||||
#define HWT_CHANNEL_B 1
|
||||
#define HWT_CHANNEL_C 2
|
||||
#define HWT_CHANNEL_D 3
|
||||
#define HWT_CHANNEL_MASK 3
|
||||
|
||||
/* The following macros are defined for timer values 1,3,4,5 */
|
||||
#define HWT_ICR(t) \
|
||||
|
|
157
dev/arduino/arduino-compat.h
Normal file
157
dev/arduino/arduino-compat.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup Hardware independent Arduino compatibility
|
||||
*
|
||||
* This file is meant to be included into a compatible Arduino.h
|
||||
*
|
||||
* Arduino calls the combination of an AVR timer and the corresponding
|
||||
* channel a timer. So Arduinos definition of timer is different from
|
||||
* that of AVR documentation. Arduino defines arbitrary TIMERXX macros
|
||||
* that are later parsed in a big switch statement.
|
||||
*
|
||||
* We use a better representation of timer values than arduino here:
|
||||
* The AVRs have max. 6 (numbered 0-5) timers. For representing these we
|
||||
* need 3 bits. In addition each timer can have channels A-D (or just
|
||||
* one channel in which case there is no alphabetic suffix). We can
|
||||
* represent this in 2 bits. We add one bit to each for future
|
||||
* compatibility and come up with 7 bits, still easily represented in 8
|
||||
* bit with room for a 'NOT_ON_TIMER' value. From these we can easily
|
||||
* compute the channel and timer by shifting. No need for a big switch
|
||||
* statement, and -- which is the common case -- when initializing with
|
||||
* a constant for the pin, the compiler can compute everything at
|
||||
* compile-time (that's why the analogWrite below is implemented as a
|
||||
* static inline function).
|
||||
*
|
||||
* Note that Arduino also defines some TIMERX without an alphabetic
|
||||
* suffix (e.g. TIMER2). I suspect this is for microcontrollers that
|
||||
* only have one channel for a certain timer. So this is currently
|
||||
* defined the same as TIMER2A because they are never used together.
|
||||
* This may be wrong and may be a bug.
|
||||
*
|
||||
* Note that the hardware definition still has to define a
|
||||
* digitalPinToTimer macro. We suggest to not implement this with a
|
||||
* static table in program memory (as currently done by arduino) but
|
||||
* instead as an if-cascade (as a C-macro). This allows the compiler to
|
||||
* completely compute the if-cascade at compile-time if the used pin is
|
||||
* a constant, resulting in *much* smaller code-footprint in the most
|
||||
* common use-case.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Header file for arduino compatibility
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/* To be included by a compatible Arduino.h */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "contiki.h"
|
||||
#include "hw_timer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#define HW_TIMER_SHIFT 3
|
||||
|
||||
#define NOT_ON_TIMER 0xFF
|
||||
|
||||
#define TIMER0A ((0 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER0B ((0 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
|
||||
#define TIMER1A ((1 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER1B ((1 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
#define TIMER1C ((1 << HW_TIMER_SHIFT) | HWT_CHANNEL_C)
|
||||
|
||||
#define TIMER2 ((2 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER2A ((2 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER2B ((2 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
|
||||
#define TIMER3A ((3 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER3B ((3 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
#define TIMER3C ((3 << HW_TIMER_SHIFT) | HWT_CHANNEL_C)
|
||||
|
||||
#define TIMER4A ((4 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER4B ((4 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
#define TIMER4C ((4 << HW_TIMER_SHIFT) | HWT_CHANNEL_C)
|
||||
#define TIMER4D ((4 << HW_TIMER_SHIFT) | HWT_CHANNEL_D)
|
||||
|
||||
#define TIMER5A ((5 << HW_TIMER_SHIFT) | HWT_CHANNEL_A)
|
||||
#define TIMER5B ((5 << HW_TIMER_SHIFT) | HWT_CHANNEL_B)
|
||||
#define TIMER5C ((5 << HW_TIMER_SHIFT) | HWT_CHANNEL_C)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void analogWrite(uint8_t pin, int val)
|
||||
{
|
||||
/*
|
||||
* Note on the timer usage: Arduino has code here that
|
||||
* explicitly checks if the given val is 0 or 0xFF.
|
||||
* The 16-bit timers on Arduino use the phase correct PWM
|
||||
* waveform generation mode which already sets the output to
|
||||
* continuous low for 0 or continuous high for 0xFF. When using
|
||||
* an 8-bit timer, Arduino uses fast PWM which creates a tiny
|
||||
* spike for 0, so to be Arduino-compatible in this mode we use
|
||||
* digitalWrite in this case.
|
||||
*/
|
||||
uint8_t arduino_timer = digitalPinToTimer(pin);
|
||||
pinMode(pin, OUTPUT);
|
||||
if (val == 0 || arduino_timer == NOT_ON_TIMER) {
|
||||
digitalWrite(pin, (val < 128) ? LOW : HIGH);
|
||||
} else {
|
||||
uint8_t t = arduino_timer >> HW_TIMER_SHIFT;
|
||||
uint8_t c = arduino_timer & HWT_CHANNEL_MASK;
|
||||
hwtimer_pwm_enable (t, c);
|
||||
hwtimer_set_pwm (t, c, val);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* VI settings, see coding style
|
||||
* ex:ts=8:et:sw=2
|
||||
*/
|
||||
|
||||
/** @} */
|
102
examples/osd/arduino-sketch/Makefile
Normal file
102
examples/osd/arduino-sketch/Makefile
Normal file
|
@ -0,0 +1,102 @@
|
|||
# Set this to the name of your sketch (without extension .pde)
|
||||
SKETCH=sketch
|
||||
|
||||
all: arduino-example \
|
||||
arduino-example.osd-merkur.hex arduino-example.osd-merkur.eep
|
||||
|
||||
# variable for this Makefile
|
||||
# configure CoAP implementation (3|7|12|13) (er-coap-07 also supports CoAP draft 08)
|
||||
WITH_COAP=13
|
||||
|
||||
# for some platforms
|
||||
UIP_CONF_IPV6=1
|
||||
# IPv6 make config disappeared completely
|
||||
CFLAGS += -DUIP_CONF_IPV6=1
|
||||
|
||||
CONTIKI=../../..
|
||||
CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\"
|
||||
|
||||
PROJECT_SOURCEFILES += resource_led_pwm.c ${SKETCH}.cpp
|
||||
|
||||
# variable for Makefile.include
|
||||
ifneq ($(TARGET), minimal-net)
|
||||
CFLAGS += -DUIP_CONF_IPV6_RPL=1
|
||||
else
|
||||
# minimal-net does not support RPL under Linux and is mostly used to test CoAP only
|
||||
${info INFO: compiling without RPL}
|
||||
CFLAGS += -DUIP_CONF_IPV6_RPL=0
|
||||
CFLAGS += -DHARD_CODED_ADDRESS=\"fdfd::10\"
|
||||
${info INFO: compiling with large buffers}
|
||||
CFLAGS += -DUIP_CONF_BUFFER_SIZE=2048
|
||||
CFLAGS += -DREST_MAX_CHUNK_SIZE=1024
|
||||
CFLAGS += -DCOAP_MAX_HEADER_SIZE=640
|
||||
endif
|
||||
|
||||
# linker optimizations
|
||||
SMALL=1
|
||||
|
||||
# REST framework, requires WITH_COAP
|
||||
ifeq ($(WITH_COAP), 13)
|
||||
${info INFO: compiling with CoAP-13}
|
||||
CFLAGS += -DWITH_COAP=13
|
||||
CFLAGS += -DREST=coap_rest_implementation
|
||||
CFLAGS += -DUIP_CONF_TCP=0
|
||||
APPS += er-coap-13
|
||||
else ifeq ($(WITH_COAP), 12)
|
||||
${info INFO: compiling with CoAP-12}
|
||||
CFLAGS += -DWITH_COAP=12
|
||||
CFLAGS += -DREST=coap_rest_implementation
|
||||
CFLAGS += -DUIP_CONF_TCP=0
|
||||
APPS += er-coap-12
|
||||
else ifeq ($(WITH_COAP), 7)
|
||||
${info INFO: compiling with CoAP-08}
|
||||
CFLAGS += -DWITH_COAP=7
|
||||
CFLAGS += -DREST=coap_rest_implementation
|
||||
CFLAGS += -DUIP_CONF_TCP=0
|
||||
APPS += er-coap-07
|
||||
else ifeq ($(WITH_COAP), 3)
|
||||
${info INFO: compiling with CoAP-03}
|
||||
CFLAGS += -DWITH_COAP=3
|
||||
CFLAGS += -DREST=coap_rest_implementation
|
||||
CFLAGS += -DUIP_CONF_TCP=0
|
||||
APPS += er-coap-03
|
||||
else
|
||||
${info INFO: compiling with HTTP}
|
||||
CFLAGS += -DWITH_HTTP
|
||||
CFLAGS += -DREST=http_rest_implementation
|
||||
CFLAGS += -DUIP_CONF_TCP=1
|
||||
APPS += er-http-engine
|
||||
endif
|
||||
|
||||
APPS += erbium time json arduino
|
||||
|
||||
include $(CONTIKI)/Makefile.include
|
||||
include $(CONTIKI)/apps/arduino/Makefile.include
|
||||
|
||||
arduino-example.osd-merkur.hex: arduino-example.osd-merkur
|
||||
avr-objcopy -j .text -j .data -O ihex arduino-example.osd-merkur \
|
||||
arduino-example.osd-merkur.hex
|
||||
|
||||
arduino-example.osd-merkur.eep: arduino-example.osd-merkur
|
||||
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
|
||||
--change-section-lma .eeprom=0 -O ihex \
|
||||
arduino-example.osd-merkur arduino-example.osd-merkur.eep
|
||||
|
||||
flash: arduino-example.osd-merkur.hex arduino-example.osd-merkur.eep
|
||||
avrdude -pm128rfa1 -c arduino -P/dev/ttyUSB0 -b57600 -e -U \
|
||||
flash:w:arduino-example.osd-merkur.hex:a -U \
|
||||
eeprom:w:arduino-example.osd-merkur.eep:a
|
||||
|
||||
.PHONY: flash
|
||||
|
||||
$(CONTIKI)/tools/tunslip6: $(CONTIKI)/tools/tunslip6.c
|
||||
(cd $(CONTIKI)/tools && $(MAKE) tunslip6)
|
||||
|
||||
connect-router: $(CONTIKI)/tools/tunslip6
|
||||
sudo $(CONTIKI)/tools/tunslip6 aaaa::1/64
|
||||
|
||||
connect-router-cooja: $(CONTIKI)/tools/tunslip6
|
||||
sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 aaaa::1/64
|
||||
|
||||
connect-minimal:
|
||||
sudo ip address add fdfd::1/64 dev tap0
|
11
examples/osd/arduino-sketch/README.md
Normal file
11
examples/osd/arduino-sketch/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
Arduino compatibility example
|
||||
=============================
|
||||
|
||||
This example shows that it is now possible to re-use arduino sketches in
|
||||
Contiki. This example documents the necessary magic. Arduino specifies
|
||||
two routines, `setup` and `loop`. Before `setup` is called, the
|
||||
framework initializes hardware. In original Arduino, all this is done in
|
||||
a `main` function (in C). For contiki we define a process that does the
|
||||
same.
|
||||
|
||||
See the documentation file in apps/contiki-compat/README.md
|
2
examples/osd/arduino-sketch/arduino-example.c
Normal file
2
examples/osd/arduino-sketch/arduino-example.c
Normal file
|
@ -0,0 +1,2 @@
|
|||
#include <arduino-process.h>
|
||||
AUTOSTART_PROCESSES(&arduino_sketch);
|
2
examples/osd/arduino-sketch/flash.sh
Executable file
2
examples/osd/arduino-sketch/flash.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
make TARGET=osd-merkur flash
|
30
examples/osd/arduino-sketch/led_pwm.h
Normal file
30
examples/osd/arduino-sketch/led_pwm.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* \defgroup Arduino LED PWM example
|
||||
*
|
||||
* Resource definition for Arduino LED PWM module
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Resource definitions for the Arduino LED PWM module
|
||||
*
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@tux.runtux.com>
|
||||
*/
|
||||
|
||||
#ifndef led_pwm_h
|
||||
#define led_pwm_h
|
||||
#include "contiki.h"
|
||||
#include "contiki-net.h"
|
||||
#include "erbium.h"
|
||||
#include "er-coap-13.h"
|
||||
|
||||
extern uint8_t pwm;
|
||||
extern uint8_t period_100ms;
|
||||
extern resource_t resource_led_pwm;
|
||||
extern resource_t resource_led_period;
|
||||
|
||||
#endif // led_pwm_h
|
||||
/** @} */
|
101
examples/osd/arduino-sketch/project-conf.h
Normal file
101
examples/osd/arduino-sketch/project-conf.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROJECT_RPL_WEB_CONF_H_
|
||||
#define PROJECT_RPL_WEB_CONF_H_
|
||||
|
||||
#define PLATFORM_HAS_LEDS 1
|
||||
//#define PLATFORM_HAS_BUTTON 1
|
||||
#define PLATFORM_HAS_BATTERY 1
|
||||
|
||||
#define SICSLOWPAN_CONF_FRAG 1
|
||||
|
||||
/* For Debug: Dont allow MCU sleeping between channel checks */
|
||||
#undef RDC_CONF_MCU_SLEEP
|
||||
#define RDC_CONF_MCU_SLEEP 0
|
||||
|
||||
/* Disabling RDC for demo purposes. Core updates often require more memory. */
|
||||
/* For projects, optimize memory and enable RDC again. */
|
||||
// #undef NETSTACK_CONF_RDC
|
||||
//#define NETSTACK_CONF_RDC nullrdc_driver
|
||||
|
||||
/* Increase rpl-border-router IP-buffer when using more than 64. */
|
||||
#undef REST_MAX_CHUNK_SIZE
|
||||
#define REST_MAX_CHUNK_SIZE 64
|
||||
|
||||
/* Estimate your header size, especially when using Proxy-Uri. */
|
||||
/*
|
||||
#undef COAP_MAX_HEADER_SIZE
|
||||
#define COAP_MAX_HEADER_SIZE 70
|
||||
*/
|
||||
|
||||
/* The IP buffer size must fit all other hops, in particular the border router. */
|
||||
|
||||
#undef UIP_CONF_BUFFER_SIZE
|
||||
#define UIP_CONF_BUFFER_SIZE 256
|
||||
|
||||
|
||||
/* Multiplies with chunk size, be aware of memory constraints. */
|
||||
#undef COAP_MAX_OPEN_TRANSACTIONS
|
||||
#define COAP_MAX_OPEN_TRANSACTIONS 4
|
||||
|
||||
/* Must be <= open transaction number, default is COAP_MAX_OPEN_TRANSACTIONS-1. */
|
||||
/*
|
||||
#undef COAP_MAX_OBSERVERS
|
||||
#define COAP_MAX_OBSERVERS 2
|
||||
*/
|
||||
|
||||
/* Filtering .well-known/core per query can be disabled to save space. */
|
||||
/*
|
||||
#undef COAP_LINK_FORMAT_FILTERING
|
||||
#define COAP_LINK_FORMAT_FILTERING 0
|
||||
*/
|
||||
|
||||
/* Save some memory for the sky platform. */
|
||||
/*
|
||||
#undef NBR_TABLE_CONF_MAX_NEIGHBORS
|
||||
#define NBR_TABLE_CONF_MAX_NEIGHBORS 10
|
||||
#undef UIP_CONF_MAX_ROUTES
|
||||
#define UIP_CONF_MAX_ROUTES 10
|
||||
*/
|
||||
|
||||
/* Reduce 802.15.4 frame queue to save RAM. */
|
||||
/*
|
||||
#undef QUEUEBUF_CONF_NUM
|
||||
#define QUEUEBUF_CONF_NUM 4
|
||||
*/
|
||||
|
||||
/*
|
||||
#undef SICSLOWPAN_CONF_FRAG
|
||||
#define SICSLOWPAN_CONF_FRAG 1
|
||||
*/
|
||||
|
||||
#endif /* PROJECT_RPL_WEB_CONF_H_ */
|
273
examples/osd/arduino-sketch/resource_led_pwm.c
Normal file
273
examples/osd/arduino-sketch/resource_led_pwm.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/**
|
||||
* \file
|
||||
* Resource for Arduino PWM
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
* \brief get/put pwm and period for LED pin
|
||||
*
|
||||
* quick&dirty implementation, this should factor json parsing.
|
||||
* But json format will probably change, there is a draft rfc.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "contiki.h"
|
||||
#include "jsonparse.h"
|
||||
/* Only coap 13 for now */
|
||||
#include "er-coap-13.h"
|
||||
#include "led_pwm.h"
|
||||
|
||||
/* Error-handling macro */
|
||||
# define BYE(_exp, _tag) \
|
||||
do { \
|
||||
PRINTF("Expect "_exp": %d\n",_tag); \
|
||||
success=0; \
|
||||
goto bye; \
|
||||
} while(0)
|
||||
|
||||
#define DEBUG 1
|
||||
#if DEBUG
|
||||
#define PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINTF(...)
|
||||
#endif
|
||||
|
||||
RESOURCE \
|
||||
( led_pwm, METHOD_GET | METHOD_PUT
|
||||
, "led/pwm"
|
||||
, "title=\"LED PWM\";rt=\"led pwm\""
|
||||
);
|
||||
|
||||
void
|
||||
led_pwm_handler
|
||||
( void* request
|
||||
, void* response
|
||||
, uint8_t *buffer
|
||||
, uint16_t preferred_size
|
||||
, int32_t *offset
|
||||
)
|
||||
{
|
||||
int success = 1;
|
||||
|
||||
int i;
|
||||
char temp[100];
|
||||
int index = 0;
|
||||
int length = 0;
|
||||
int tag = 0;
|
||||
const uint8_t *bytes = NULL;
|
||||
size_t len = 0;
|
||||
int n_acc = 0;
|
||||
const uint16_t *accept = NULL;
|
||||
uint16_t a_ctype = REST.type.APPLICATION_JSON;
|
||||
uint16_t c_ctype = REST.get_header_content_type (request);
|
||||
|
||||
/* Seems like accepted type is currently unsupported? */
|
||||
n_acc = REST.get_header_accept (request, &accept);
|
||||
for (i=0; i<n_acc; i++) {
|
||||
if ( accept [i] == REST.type.TEXT_PLAIN
|
||||
|| accept [i] == REST.type.APPLICATION_JSON
|
||||
)
|
||||
{
|
||||
a_ctype = accept [i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(REST.get_method_type(request)) {
|
||||
case METHOD_GET:
|
||||
// TEXT format
|
||||
if (a_ctype == REST.type.TEXT_PLAIN) {
|
||||
index += sprintf (temp + index, "%d\n", pwm);
|
||||
} else { // jSON Format
|
||||
index += sprintf
|
||||
( temp + index
|
||||
,"{\n \"pwm\" : \"%d\"\n}\n"
|
||||
, pwm
|
||||
);
|
||||
}
|
||||
length = strlen(temp);
|
||||
memcpy (buffer, temp, length);
|
||||
REST.set_header_content_type (response, a_ctype);
|
||||
REST.set_response_payload (response, buffer, length);
|
||||
|
||||
break;
|
||||
case METHOD_PUT:
|
||||
if ((len = coap_get_payload(request, &bytes))) {
|
||||
PRINTF ("PUT: len: %d, %s\n", len, (char *)bytes);
|
||||
if (c_ctype == REST.type.TEXT_PLAIN) {
|
||||
temp [sizeof (temp) - 1] = 0;
|
||||
strncpy (temp, (char *)bytes, MIN (len + 1, sizeof (temp) - 1));
|
||||
} else { // jSON Format
|
||||
struct jsonparse_state state;
|
||||
struct jsonparse_state *parser = &state;
|
||||
jsonparse_setup (parser, (char *)bytes, len);
|
||||
if ((tag = jsonparse_next (parser)) != JSON_TYPE_OBJECT) {
|
||||
BYE ("OBJECT", tag);
|
||||
}
|
||||
if ((tag = jsonparse_next (parser)) != JSON_TYPE_PAIR_NAME) {
|
||||
BYE ("PAIR_NAME", tag);
|
||||
}
|
||||
while (jsonparse_strcmp_value (parser, "pwm") != 0) {
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR) {
|
||||
BYE ("PAIR", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != ',') {
|
||||
BYE (",", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR_NAME) {
|
||||
BYE ("PAIR_NAME", tag);
|
||||
}
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR) {
|
||||
BYE ("PAIR", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_STRING) {
|
||||
BYE ("STRING", tag);
|
||||
}
|
||||
jsonparse_copy_value (parser, temp, sizeof (temp));
|
||||
temp [sizeof (temp) - 1] = 0;
|
||||
}
|
||||
PRINTF ("GOT: %s\n", temp);
|
||||
pwm = atoi (temp);
|
||||
PRINTF ("Setting: %d\n", pwm);
|
||||
REST.set_response_status(response, REST.status.CHANGED);
|
||||
} else {
|
||||
PRINTF ("PUT: len: %d\n", len);
|
||||
success = 0;
|
||||
}
|
||||
bye :
|
||||
break;
|
||||
default:
|
||||
success = 0;
|
||||
}
|
||||
if (!success) {
|
||||
REST.set_response_status(response, REST.status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
RESOURCE \
|
||||
( led_period, METHOD_GET | METHOD_PUT
|
||||
, "led/period"
|
||||
, "title=\"LED Period\";rt=\"led period\""
|
||||
);
|
||||
|
||||
void
|
||||
led_period_handler
|
||||
( void* request
|
||||
, void* response
|
||||
, uint8_t *buffer
|
||||
, uint16_t preferred_size
|
||||
, int32_t *offset
|
||||
)
|
||||
{
|
||||
int success = 1;
|
||||
|
||||
int i;
|
||||
char temp[100];
|
||||
int index = 0;
|
||||
int length = 0;
|
||||
int tag = 0;
|
||||
const uint8_t *bytes = NULL;
|
||||
size_t len = 0;
|
||||
int n_acc = 0;
|
||||
const uint16_t *accept = NULL;
|
||||
uint16_t a_ctype = REST.type.APPLICATION_JSON;
|
||||
uint16_t c_ctype = REST.get_header_content_type (request);
|
||||
|
||||
/* Seems like accepted type is currently unsupported? */
|
||||
n_acc = REST.get_header_accept (request, &accept);
|
||||
for (i=0; i<n_acc; i++) {
|
||||
if ( accept [i] == REST.type.TEXT_PLAIN
|
||||
|| accept [i] == REST.type.APPLICATION_JSON
|
||||
)
|
||||
{
|
||||
a_ctype = accept [i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(REST.get_method_type(request)) {
|
||||
case METHOD_GET:
|
||||
// TEXT format
|
||||
if (a_ctype == REST.type.TEXT_PLAIN) {
|
||||
index += sprintf (temp + index, "%d\n", period_100ms * 100);
|
||||
} else { // jSON Format
|
||||
index += sprintf
|
||||
( temp + index
|
||||
,"{\n \"period\" : \"%d\"\n}\n"
|
||||
, period_100ms * 100
|
||||
);
|
||||
}
|
||||
length = strlen(temp);
|
||||
memcpy (buffer, temp, length);
|
||||
REST.set_header_content_type (response, a_ctype);
|
||||
REST.set_response_payload (response, buffer, length);
|
||||
|
||||
break;
|
||||
case METHOD_PUT:
|
||||
if ((len = coap_get_payload(request, &bytes))) {
|
||||
PRINTF ("PUT: len: %d, %s\n", len, (char *)bytes);
|
||||
if (c_ctype == REST.type.TEXT_PLAIN) {
|
||||
temp [sizeof (temp) - 1] = 0;
|
||||
strncpy (temp, (char *)bytes, MIN (len + 1, sizeof (temp) - 1));
|
||||
} else { // jSON Format
|
||||
struct jsonparse_state state;
|
||||
struct jsonparse_state *parser = &state;
|
||||
jsonparse_setup (parser, (char *)bytes, len);
|
||||
if ((tag = jsonparse_next (parser)) != JSON_TYPE_OBJECT) {
|
||||
BYE ("OBJECT", tag);
|
||||
}
|
||||
if ((tag = jsonparse_next (parser)) != JSON_TYPE_PAIR_NAME) {
|
||||
BYE ("PAIR_NAME", tag);
|
||||
}
|
||||
while (jsonparse_strcmp_value (parser, "period") != 0) {
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR) {
|
||||
BYE ("PAIR", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != ',') {
|
||||
BYE (",", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR_NAME) {
|
||||
BYE ("PAIR_NAME", tag);
|
||||
}
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_PAIR) {
|
||||
BYE ("PAIR", tag);
|
||||
}
|
||||
tag = jsonparse_next (parser);
|
||||
if (tag != JSON_TYPE_STRING) {
|
||||
BYE ("STRING", tag);
|
||||
}
|
||||
jsonparse_copy_value (parser, temp, sizeof (temp));
|
||||
temp [sizeof (temp) - 1] = 0;
|
||||
}
|
||||
PRINTF ("GOT: %s\n", temp);
|
||||
period_100ms = (atoi (temp) + 50) / 100;
|
||||
PRINTF ("Setting: %dms\n", period_100ms * 100);
|
||||
REST.set_response_status(response, REST.status.CHANGED);
|
||||
} else {
|
||||
PRINTF ("PUT: len: %d\n", len);
|
||||
success = 0;
|
||||
}
|
||||
bye :
|
||||
break;
|
||||
default:
|
||||
success = 0;
|
||||
}
|
||||
if (!success) {
|
||||
REST.set_response_status(response, REST.status.BAD_REQUEST);
|
||||
}
|
||||
}
|
5
examples/osd/arduino-sketch/run.sh
Executable file
5
examples/osd/arduino-sketch/run.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
# For the new bootloader (using a jump-table) you want to use
|
||||
# BOOTLOADER_GET_MAC=0x0001ff80 (which is the current default)
|
||||
make clean TARGET=osd-merkur
|
||||
make TARGET=osd-merkur BOOTLOADER_GET_MAC=0x0001f3a0
|
47
examples/osd/arduino-sketch/sketch.pde
Normal file
47
examples/osd/arduino-sketch/sketch.pde
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Sample arduino sketch using contiki features.
|
||||
* We turn the LED on and off and allow setting the interval and the
|
||||
* brightness of the LED via coap.
|
||||
* Unfortunately sleeping for long times in loop() isn't currently
|
||||
* possible, something turns off the CPU (including PWM outputs) if a
|
||||
* Proto-Thread is taking too long. We need to find out how to sleep in
|
||||
* a Contiki-compatible way.
|
||||
* Note that for a normal arduino sketch you won't have to include any
|
||||
* of the contiki-specific files here, the sketch should just work.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
#include "led_pwm.h"
|
||||
#define LED_PIN 5
|
||||
|
||||
uint8_t pwm = 128;
|
||||
uint8_t period_100ms = 10; /* one second */
|
||||
}
|
||||
|
||||
void setup (void)
|
||||
{
|
||||
rest_init_engine ();
|
||||
rest_activate_resource (&resource_led_pwm);
|
||||
rest_activate_resource (&resource_led_period);
|
||||
}
|
||||
|
||||
void loop (void)
|
||||
{
|
||||
static uint8_t last_pwm = 0;
|
||||
if (last_pwm != pwm) {
|
||||
last_pwm = pwm;
|
||||
analogWrite (LED_PIN, pwm);
|
||||
printf
|
||||
( "TCNT3: %04X TCCR3A: %04X TCCR3B: %04X TCCR3C: %04X OCR3C: %04X\n"
|
||||
, TCNT3, TCCR3A, TCCR3B, TCCR3C, OCR3C
|
||||
);
|
||||
}
|
||||
|
||||
// Originally I wanted to sleep here to make the LED blink.
|
||||
// Sleeping currently doesn't work, something turns off the chip.
|
||||
// Maybe a mechanism to guard agains proto-threads taking too long?
|
||||
//clock_wait (CLOCK_SECOND * period_100ms / 10);
|
||||
//analogWrite (LED_PIN, 0);
|
||||
//printf ("After write\n");
|
||||
}
|
|
@ -27,7 +27,7 @@ CONTIKI_TARGET_SOURCEFILES += servo.c servo-sensor.c
|
|||
#Needed for Relay 1 to 4
|
||||
CONTIKI_TARGET_SOURCEFILES += relay.c relay-sensor.c
|
||||
# Arduino
|
||||
CONTIKI_TARGET_SOURCEFILES += wiring_digital.c
|
||||
CONTIKI_TARGET_SOURCEFILES += wiring_digital.c wiring.c wiring_analog.c
|
||||
|
||||
CONTIKIBOARD=.
|
||||
BOOTLOADER_START = 0x1F000
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
|
||||
#include <hw-arduino.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
@ -88,14 +89,18 @@ typedef unsigned int word;
|
|||
typedef uint8_t boolean;
|
||||
typedef uint8_t byte;
|
||||
|
||||
void init(void);
|
||||
/*
|
||||
* This has been renamed from init to arduino_init, the original
|
||||
* function name is way too generic. The arduino compatibility framework
|
||||
* makes sure the correct function is called.
|
||||
*/
|
||||
void arduino_init(void);
|
||||
|
||||
void pinMode(uint8_t, uint8_t);
|
||||
void digitalWrite(uint8_t, uint8_t);
|
||||
int digitalRead(uint8_t);
|
||||
int analogRead(uint8_t);
|
||||
void analogReference(uint8_t mode);
|
||||
void analogWrite(uint8_t, int);
|
||||
|
||||
unsigned long millis(void);
|
||||
unsigned long micros(void);
|
||||
|
@ -126,7 +131,6 @@ extern const uint16_t PROGMEM port_to_output_PGM[];
|
|||
extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
|
||||
// extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
|
||||
extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
|
||||
extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
|
||||
|
||||
// Get the bit location within the hardware port of the given virtual pin.
|
||||
// This comes from the pins_*.c file for the active board configuration.
|
||||
|
@ -135,7 +139,6 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
|
|||
//
|
||||
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
|
||||
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
|
||||
#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
|
||||
#define analogInPinToBit(P) (P)
|
||||
#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
|
||||
#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
|
||||
|
@ -158,35 +161,15 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
|
|||
#define PL 12
|
||||
#endif
|
||||
|
||||
#define NOT_ON_TIMER 0
|
||||
#define TIMER0A 1
|
||||
#define TIMER0B 2
|
||||
#define TIMER1A 3
|
||||
#define TIMER1B 4
|
||||
#define TIMER1C 5
|
||||
#define TIMER2 6
|
||||
#define TIMER2A 7
|
||||
#define TIMER2B 8
|
||||
|
||||
#define TIMER3A 9
|
||||
#define TIMER3B 10
|
||||
#define TIMER3C 11
|
||||
#define TIMER4A 12
|
||||
#define TIMER4B 13
|
||||
#define TIMER4C 14
|
||||
#define TIMER4D 15
|
||||
#define TIMER5A 16
|
||||
#define TIMER5B 17
|
||||
#define TIMER5C 18
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "WCharacter.h"
|
||||
#include "WString.h"
|
||||
#include "HardwareSerial.h"
|
||||
// look at this again when considering implementing serial
|
||||
//#include "WCharacter.h"
|
||||
//#include "WString.h"
|
||||
//#include "HardwareSerial.h"
|
||||
|
||||
uint16_t makeWord(uint16_t w);
|
||||
uint16_t makeWord(byte h, byte l);
|
||||
|
@ -208,4 +191,6 @@ long map(long, long, long, long, long);
|
|||
|
||||
#include "pins_arduino.h"
|
||||
|
||||
#endif
|
||||
#include "dev/arduino/arduino-compat.h"
|
||||
|
||||
#endif
|
||||
|
|
73
platform/osd-merkur/dev/hw-arduino.h
Normal file
73
platform/osd-merkur/dev/hw-arduino.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup compatibility Arduino - Contiki
|
||||
*
|
||||
* This defines contiki-compatible hardware definitions for running
|
||||
* arduino sketches (or just to call arduino-compatible function).
|
||||
* For now only for osd hardware, a similar file should exist for each
|
||||
* arduino-compatible hardware.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Header file for arduino compatibility
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The OSD hardware only supports timer 3 for PWM, timer 2 is used by
|
||||
* contiki for sleep/wakeup timing and is not usable for PWM.
|
||||
*/
|
||||
#define digitalPinToTimer(pin) \
|
||||
( (pin) == 3 \
|
||||
? TIMER3A \
|
||||
: ( (pin) == 4 \
|
||||
? TIMER3B \
|
||||
: ((pin == 5) ? TIMER3C : NOT_ON_TIMER) \
|
||||
) \
|
||||
)
|
||||
|
||||
/* Only init timer 3 with phase correct pwm 8-bit and prescaler 64 */
|
||||
#define arduino_pwm_timer_init() \
|
||||
(hwtimer_ini (3, HWT_WGM_PWM_PHASE_8_BIT, HWT_CLOCK_PRESCALER_64, 0))
|
||||
|
||||
/*
|
||||
* VI settings, see coding style
|
||||
* ex:ts=8:et:sw=2
|
||||
*/
|
||||
|
||||
/** @} */
|
|
@ -215,48 +215,6 @@ const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
|
|||
// _BV( 7 ) , // PB 7 ** 35 ** D35 / LED2 / PWM
|
||||
};
|
||||
|
||||
// !!!
|
||||
const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
|
||||
// TIMERS
|
||||
// -------------------------------------------
|
||||
NOT_ON_TIMER , // PE 1 ** 0 ** USART0_TX
|
||||
NOT_ON_TIMER , // PE 0 ** 1 ** USART0_RX
|
||||
TIMER3A , // PE 3 ** 2 ** D3 / PWM
|
||||
TIMER3B , // PE 4 ** 3 ** D4 / PWM
|
||||
TIMER3C , // PE 5 ** 4 ** D5 / PWM
|
||||
NOT_ON_TIMER , // PE 6 ** 5 ** D6
|
||||
NOT_ON_TIMER , // PD 3 ** 6 ** D21 / USART1_TX
|
||||
NOT_ON_TIMER , // PD 2 ** 7 ** D20 / USART1_RX
|
||||
NOT_ON_TIMER , // PD 0 ** 8 ** D15 / I2C_SCL
|
||||
NOT_ON_TIMER , // PD 1 ** 9 ** D14 / I2C_SDA
|
||||
NOT_ON_TIMER , // PB 0 ** 10 ** D10 / SPI_SSN
|
||||
NOT_ON_TIMER , // PB 2 ** 11 ** D11 / SPI_MOSI
|
||||
NOT_ON_TIMER , // PB 1 ** 12 ** D13 / SPI_SCK
|
||||
NOT_ON_TIMER , // PB 3 ** 13 ** D12 / SPI_MISO
|
||||
TIMER2A , // PB 4 ** 14 ** D9 / PWM
|
||||
NOT_ON_TIMER , // PF 7 ** 15 ** A0 / D33
|
||||
NOT_ON_TIMER , // PF 6 ** 16 ** A1 / D32
|
||||
NOT_ON_TIMER , // PF 5 ** 17 ** A2 / D31
|
||||
NOT_ON_TIMER , // PF 4 ** 18 ** A3 / D30
|
||||
NOT_ON_TIMER , // PF 0 ** 19 ** A4 / D26
|
||||
NOT_ON_TIMER , // PF 1 ** 20 ** A5 / D27
|
||||
// NOT_ON_TIMER , // PF 2 ** 28 ** A6 / D28
|
||||
// NOT_ON_TIMER , // PF 3 ** 29 ** A7 / D29
|
||||
// NOT_ON_TIMER , // PE 2 ** 2 ** D2
|
||||
// NOT_ON_TIMER , // PE 7 ** 7 ** D7
|
||||
// TIMER1A , // PB 5 ** 8 ** D8 / PWM
|
||||
// NOT_ON_TIMER , // PG 0 ** 16 ** D16
|
||||
// NOT_ON_TIMER , // PG 1 ** 17 ** D17
|
||||
// NOT_ON_TIMER , // PG 2 ** 18 ** D18
|
||||
// TIMER0B , // PG 5 ** 19 ** D19 / PWM
|
||||
// NOT_ON_TIMER , // PD 4 ** 22 ** D22
|
||||
// NOT_ON_TIMER , // PD 5 ** 23 ** D23
|
||||
// NOT_ON_TIMER , // PD 6 ** 24 ** D24
|
||||
// NOT_ON_TIMER , // PD 7 ** 25 ** D25
|
||||
// TIMER1B , // PB 6 ** 34 ** D34/ PWM
|
||||
// TIMER1C , // PB 7 ** 35 ** D35 / PWM
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include "wiring_private.h"
|
||||
#include "hw-arduino.h"
|
||||
|
||||
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
|
||||
// the overflow handler is called every 256 ticks.
|
||||
|
@ -186,20 +187,23 @@ void delayMicroseconds(unsigned int us)
|
|||
);
|
||||
}
|
||||
|
||||
void init()
|
||||
void arduino_init()
|
||||
{
|
||||
// this needs to be called before setup() or some functions won't
|
||||
// work there
|
||||
sei();
|
||||
|
||||
// on the ATmega168, timer 0 is also used for fast hardware pwm
|
||||
// (using phase-correct PWM would mean that timer 0 overflowed half as often
|
||||
// resulting in different millis() behavior on the ATmega8 and ATmega168)
|
||||
/*
|
||||
* RSC: Keep timer0 for now, until we decide how to implement
|
||||
* millis() etc in a contiki-compatible way
|
||||
*/
|
||||
|
||||
#if defined(TCCR0A) && defined(WGM01)
|
||||
sbi(TCCR0A, WGM01);
|
||||
sbi(TCCR0A, WGM00);
|
||||
#endif
|
||||
|
||||
// set timer 0 prescale factor to 64
|
||||
#if defined(__AVR_ATmega128__)
|
||||
// CPU specific: different values for the ATmega128
|
||||
|
@ -229,96 +233,13 @@ void init()
|
|||
#error Timer 0 overflow interrupt not set correctly
|
||||
#endif
|
||||
|
||||
// timers 1 and 2 are used for phase-correct hardware pwm
|
||||
// this is better for motors as it ensures an even waveform
|
||||
// note, however, that fast pwm mode can achieve a frequency of up
|
||||
// 8 MHz (with a 16 MHz clock) at 50% duty cycle
|
||||
/*
|
||||
* All other PCM timers are initialized here in a
|
||||
* platform-specific way
|
||||
*/
|
||||
arduino_pwm_timer_init ();
|
||||
|
||||
#if defined(TCCR1B) && defined(CS11) && defined(CS10)
|
||||
TCCR1B = 0;
|
||||
|
||||
// set timer 1 prescale factor to 64
|
||||
sbi(TCCR1B, CS11);
|
||||
#if F_CPU >= 8000000L
|
||||
sbi(TCCR1B, CS10);
|
||||
#endif
|
||||
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
|
||||
sbi(TCCR1, CS11);
|
||||
#if F_CPU >= 8000000L
|
||||
sbi(TCCR1, CS10);
|
||||
#endif
|
||||
#endif
|
||||
// put timer 1 in 8-bit phase correct pwm mode
|
||||
#if defined(TCCR1A) && defined(WGM10)
|
||||
sbi(TCCR1A, WGM10);
|
||||
#elif defined(TCCR1)
|
||||
#warning this needs to be finished
|
||||
#endif
|
||||
|
||||
// set timer 2 prescale factor to 64
|
||||
#if defined(TCCR2) && defined(CS22)
|
||||
sbi(TCCR2, CS22);
|
||||
#elif defined(TCCR2B) && defined(CS22)
|
||||
sbi(TCCR2B, CS22);
|
||||
#else
|
||||
#warning Timer 2 not finished (may not be present on this CPU)
|
||||
#endif
|
||||
|
||||
// configure timer 2 for phase correct pwm (8-bit)
|
||||
#if defined(TCCR2) && defined(WGM20)
|
||||
sbi(TCCR2, WGM20);
|
||||
#elif defined(TCCR2A) && defined(WGM20)
|
||||
sbi(TCCR2A, WGM20);
|
||||
#else
|
||||
#warning Timer 2 not finished (may not be present on this CPU)
|
||||
#endif
|
||||
|
||||
#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
|
||||
sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64
|
||||
sbi(TCCR3B, CS30);
|
||||
sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode
|
||||
#endif
|
||||
|
||||
#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
|
||||
sbi(TCCR4B, CS42); // set timer4 prescale factor to 64
|
||||
sbi(TCCR4B, CS41);
|
||||
sbi(TCCR4B, CS40);
|
||||
sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode
|
||||
sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A
|
||||
sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D
|
||||
#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
|
||||
#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
|
||||
sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64
|
||||
sbi(TCCR4B, CS40);
|
||||
sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode
|
||||
#endif
|
||||
#endif /* end timer4 block for ATMEGA1280/2560 and similar */
|
||||
|
||||
#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
|
||||
sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64
|
||||
sbi(TCCR5B, CS50);
|
||||
sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode
|
||||
#endif
|
||||
|
||||
#if defined(ADCSRA)
|
||||
// set a2d prescale factor to 128
|
||||
// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
|
||||
// XXX: this will not work properly for other clock speeds, and
|
||||
// this code should use F_CPU to determine the prescale factor.
|
||||
sbi(ADCSRA, ADPS2);
|
||||
sbi(ADCSRA, ADPS1);
|
||||
sbi(ADCSRA, ADPS0);
|
||||
|
||||
// enable a2d conversions
|
||||
sbi(ADCSRA, ADEN);
|
||||
#endif
|
||||
|
||||
// the bootloader connects pins 0 and 1 to the USART; disconnect them
|
||||
// here so they can be used as normal digital i/o; they will be
|
||||
// reconnected in Serial.begin()
|
||||
#if defined(UCSRB)
|
||||
UCSRB = 0;
|
||||
#elif defined(UCSR0B)
|
||||
UCSR0B = 0;
|
||||
#endif
|
||||
/*
|
||||
* Removed the rest which manipulates the serial pins
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -43,198 +43,6 @@ int analogRead(uint8_t pin)
|
|||
return readADC(pin);
|
||||
}
|
||||
|
||||
// Right now, PWM output only works on the pins with
|
||||
// hardware support. These are defined in the appropriate
|
||||
// pins_*.c file. For the rest of the pins, we default
|
||||
// to digital output.
|
||||
void analogWrite(uint8_t pin, int val)
|
||||
{
|
||||
// We need to make sure the PWM output is enabled for those pins
|
||||
// that support it, as we turn it off when digitally reading or
|
||||
// writing with them. Also, make sure the pin is in output mode
|
||||
// for consistenty with Wiring, which doesn't require a pinMode
|
||||
// call for the analog output pins.
|
||||
pinMode(pin, OUTPUT);
|
||||
if (val == 0)
|
||||
{
|
||||
digitalWrite(pin, LOW);
|
||||
}
|
||||
else if (val == 255)
|
||||
{
|
||||
digitalWrite(pin, HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(digitalPinToTimer(pin))
|
||||
{
|
||||
// XXX fix needed for atmega8
|
||||
#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
|
||||
case TIMER0A:
|
||||
// connect pwm to pin on timer 0
|
||||
sbi(TCCR0, COM00);
|
||||
OCR0 = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR0A) && defined(COM0A1)
|
||||
case TIMER0A:
|
||||
// connect pwm to pin on timer 0, channel A
|
||||
sbi(TCCR0A, COM0A1);
|
||||
OCR0A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR0A) && defined(COM0B1)
|
||||
case TIMER0B:
|
||||
// connect pwm to pin on timer 0, channel B
|
||||
sbi(TCCR0A, COM0B1);
|
||||
OCR0B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR1A) && defined(COM1A1)
|
||||
case TIMER1A:
|
||||
// connect pwm to pin on timer 1, channel A
|
||||
sbi(TCCR1A, COM1A1);
|
||||
OCR1A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR1A) && defined(COM1B1)
|
||||
case TIMER1B:
|
||||
// connect pwm to pin on timer 1, channel B
|
||||
sbi(TCCR1A, COM1B1);
|
||||
OCR1B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR1A) && defined(COM1C1)
|
||||
case TIMER1C:
|
||||
// connect pwm to pin on timer 1, channel C
|
||||
sbi(TCCR1A, COM1C1);
|
||||
OCR1C = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR2) && defined(COM21)
|
||||
case TIMER2:
|
||||
// connect pwm to pin on timer 2
|
||||
sbi(TCCR2, COM21);
|
||||
OCR2 = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR2A) && defined(COM2A1)
|
||||
case TIMER2A:
|
||||
// connect pwm to pin on timer 2, channel A
|
||||
sbi(TCCR2A, COM2A1);
|
||||
OCR2A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR2A) && defined(COM2B1)
|
||||
case TIMER2B:
|
||||
// connect pwm to pin on timer 2, channel B
|
||||
sbi(TCCR2A, COM2B1);
|
||||
OCR2B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR3A) && defined(COM3A1)
|
||||
case TIMER3A:
|
||||
// connect pwm to pin on timer 3, channel A
|
||||
sbi(TCCR3A, COM3A1);
|
||||
OCR3A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR3A) && defined(COM3B1)
|
||||
case TIMER3B:
|
||||
// connect pwm to pin on timer 3, channel B
|
||||
sbi(TCCR3A, COM3B1);
|
||||
OCR3B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR3A) && defined(COM3C1)
|
||||
case TIMER3C:
|
||||
// connect pwm to pin on timer 3, channel C
|
||||
sbi(TCCR3A, COM3C1);
|
||||
OCR3C = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR4A)
|
||||
case TIMER4A:
|
||||
//connect pwm to pin on timer 4, channel A
|
||||
sbi(TCCR4A, COM4A1);
|
||||
#if defined(COM4A0) // only used on 32U4
|
||||
cbi(TCCR4A, COM4A0);
|
||||
#endif
|
||||
OCR4A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR4A) && defined(COM4B1)
|
||||
case TIMER4B:
|
||||
// connect pwm to pin on timer 4, channel B
|
||||
sbi(TCCR4A, COM4B1);
|
||||
OCR4B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR4A) && defined(COM4C1)
|
||||
case TIMER4C:
|
||||
// connect pwm to pin on timer 4, channel C
|
||||
sbi(TCCR4A, COM4C1);
|
||||
OCR4C = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR4C) && defined(COM4D1)
|
||||
case TIMER4D:
|
||||
// connect pwm to pin on timer 4, channel D
|
||||
sbi(TCCR4C, COM4D1);
|
||||
#if defined(COM4D0) // only used on 32U4
|
||||
cbi(TCCR4C, COM4D0);
|
||||
#endif
|
||||
OCR4D = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(TCCR5A) && defined(COM5A1)
|
||||
case TIMER5A:
|
||||
// connect pwm to pin on timer 5, channel A
|
||||
sbi(TCCR5A, COM5A1);
|
||||
OCR5A = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR5A) && defined(COM5B1)
|
||||
case TIMER5B:
|
||||
// connect pwm to pin on timer 5, channel B
|
||||
sbi(TCCR5A, COM5B1);
|
||||
OCR5B = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(TCCR5A) && defined(COM5C1)
|
||||
case TIMER5C:
|
||||
// connect pwm to pin on timer 5, channel C
|
||||
sbi(TCCR5A, COM5C1);
|
||||
OCR5C = val; // set pwm duty
|
||||
break;
|
||||
#endif
|
||||
|
||||
case NOT_ON_TIMER:
|
||||
default:
|
||||
if (val < 128) {
|
||||
digitalWrite(pin, LOW);
|
||||
} else {
|
||||
digitalWrite(pin, HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* analogWrite is now implemented in dev/arduino/arduino-compat.h
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue