diff --git a/LICENSE.md b/LICENSE.md index 514d025..e49d082 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Electric Imp +Copyright (c) 2016 Electric Imp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/hardware/impeeduino/.gitignore b/hardware/impeeduino/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/hardware/impeeduino/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/hardware/impeeduino/LICENSE b/hardware/impeeduino/LICENSE new file mode 100644 index 0000000..e49d082 --- /dev/null +++ b/hardware/impeeduino/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Electric Imp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/hardware/impeeduino/README.md b/hardware/impeeduino/README.md index 5f0bd63..cda05cc 100644 --- a/hardware/impeeduino/README.md +++ b/hardware/impeeduino/README.md @@ -1,32 +1,163 @@ -Impeedunio Programmer -===================== +# Impeeduino 1.0.0 -This firmware will allow you to program the ATmega328 built into the Impduino. -It parses Intel HEX files delivered via HTTP POST (form) and implements the STK500v1 serial protocol to talk to the connected ATmega328. +The Impeeduino is a combination of an imp001 and an Arduino. This library provides a simple way to instruct the Arduino side of the Impeeduino to perform basic tasks over UART. -You will need to install the "[optiboot](https://code.google.com/p/optiboot/)" bootloader using an ICSP cable. At the time of writing, the latest version was [v5.0a](https://code.google.com/p/optiboot/downloads/detail?name=optiboot-v5.0a.zip). -To do this you will need an ISP or use [another Ardiuno as the ISP](http://arduino.cc/en/Tutorial/ArduinoISP) and the ArduinoISP sketch. +The Impeeduino squirrel library contained in `impeeduino.class.nut` pairs with the [impeeduino Arduino project](./arduino/impeeduino). More details on the underlying UART communication scheme and instructions on taking advantage of the user-modifiable function call system can be found in the corresponding [Readme file](./arduino/impeeduino/README.md). -You might need to adjust the signature of the ATmega328P in the avrdude configuration to make avrdude think its an ATmega328P. -/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf +To upload Arduino code onto the Impeeduino's ATmega328, an [Impeeduino Programmer model](./programmer) is provided. This model includes agent and device code for programming the Arduino via an Imp with a HEX file generated from the Arduino IDE. - From: signature = 0x1e 0x95 0x0f; - To: signature = 0x1e 0x95 0x14; + -Once the ATMega is programmed you can continue to talk to it over the serial port. +## Class Usage -Note, if you are using the [SparkFun Imp Shield](https://www.sparkfun.com/products/11401) then you may need to reverse the -logic of the RESET pin. Where you see RESET.write(1), change it to RESET.write(0) and vice versa. +### Constructor: Impeeduino(*[serial, reset]*) +The constructor takes two arguments to instantiate the class: a non-configured UART bus and a GPIO pin connected to the Arduino's reset line. These default to the configuration used on the Impeeduino rev2, using `uart57` for serial communications and `pin1` for the reset line. -Contributors -============ +```squirrel +impeeduino <- Impeeduino(); -- Aron +// Equivalent, but more verbose instantiation: +impeeduino <- Impeeduino(hardware.uart57, hardware.pin1); +``` -Usage -===== +## Class Methods -This is not a library class. It represents an entire application for programming the Arduino via an Imp with a HEX file. -You can adapt it to your needs, such as combining the programming functionality with application level communication. +### reset() +Resets the ATMega processor by bouncing the reset pin. Note that reseting will block the imp for about 0.2 seconds. + +```squirrel +impeeduino.reset(); +``` + +### pinMode(*pin, mode*) + +Configures the specified GPIO pin to the specified mode. Possible configurations are input, input with pullup, output, and PWM output. The relevant constants are summarized below. Note that not all ATMega pins are capable of being used for PWM output. + +| Mode Constant | Configuration | +| ------------- | ------------- | +| DIGITAL_IN | Input (High Impedance) | +| DIGITAL_IN_PULLUP | Input with pullup | +| DIGITAL_OUT | Digital output | +| PWM_OUT | PWM output | + + +```squirrel +// Configure pin 2 as digital input +impeeduino.pinMode(2, DIGITAL_IN); + +// Configure pin 3 as digital input with pullup +impeeduino.pinMode(3, DIGITAL_IN_PULLUP); + +// Configure pin 4 as digital output +impeeduino.pinMode(4, DIGITAL_OUT); + +// Configure pin 5 as PWM output +impeeduino.pinMode(5, PWM_OUT); +``` + +### digitalWrite(*pin, value*) + +Writes a value to the specified digital pin. Value can be either a boolean or an integer value. For boolean values, true corresponding to high and false to low. For integers, non-zero values correspond to high and zero corresponds to false. + +```squirrel +// Set pin 4 to HIGH +digitalWrite(4, 1); + +// Set pin 4 to LOW +digitalWrite(4, 0); + +// Toggle pin 4 every 2 seconds +isOn <- true; + +function blink() { + digitalWrite(4, isOn); + isOn = !isOn; + imp.wakeup(2, blink); +} + +blink(); +``` + +### analogWrite(*pin, value*) + +Writes an analog value (PWM wave) to a pin. *value* is an integer value representing the duty cycle and ranges between 0 (off) and 255 (always on). For compatibility with imp code, value may also be a floating point duty ratio from 0.0 to 1.0. This is then rounded to the nearest available value. + +```squirrel +// Configure pin 5 as pwm output +impeeduino.pinMode(5, PWM_OUT); + +// Set pin 5 to 50% duty cycle +impeeduino.analogWrite(5, 0.5); + +// Set pin 5 to 0% duty cycle (always off) +impeeduino.analogWrite(5, 0); + +// Set pin 5 to 100% duty cycle (always on) +impeeduino.analogWrite(5, 255); +``` + +### digitalRead(*pin[, callback]*) + +Reads the logical value of the specified digital pin and returns it as an integer. A value of 0 corresponds to digital low, a value of 1 corresponds to digital high. + +If a callback parameter is provided, the reading executes asynchronously and the resulting integer value will be passed to the supplied function as the only parameter. If no callback is provided, the method blocks until the reading has been taken. + +#### Asynchronous Example + +```squirrel +// Read pin 2 asynchronously and log the returned value +impeeduino.digitalRead(2, function(value) { + server.log("Async digital read: Pin 2 has value " + value); +}); +``` + +#### Synchronous Example + +```squirrel +// Read pin 2 and log the returned value +server.log("Digital read: Pin 2 has value " + impeeduino.digitalRead(2)); +``` + +### analogRead(*pin[, callback]*) + +Reads the value of the specified analog pin and returns it as an integer. The Arduino has a 10-bit ADC, so returned values will range from 0 to 1023. + +If a callback parameter is provided, the reading executes asynchronously and the resulting integer value will be passed to the supplied function as the only parameter. If no callback is provided, the method blocks until the reading has been taken. + +#### Asynchronous Example + +```squirrel +// Read analog pin 0 asynchronously and log the returned value +impeeduino.analogRead(0, function(value) { + server.log("Async analog read: Pin A0 has value " + value); +}); +``` + +#### Synchronous Example + +```squirrel +// Read analog pin 0 and log the returned value +server.log("Analog read: Pin A0 has value " + impeeduino.analogRead(0)); +``` +### functionCall(*id[, argument, callback]*) + +Performs a function call on the Arduino. This is intended as a way to trigger additional functionality on the Arduino. There are 30 user-modifiable custom functions available in the Arduino code, with id numbers 1-30. + +Each function may take an ASCII string argument and optionally pass back an ASCII string return value. Because the underlying serial communication scheme uses the most significant bit to indicate commands, only standard ASCII characters (value 0-127) may be sent. + +Arduino function calls are asynchronous. The returned string will be passed to as the sole parameter to the optional callback as a binary blob. + +```squirrel +server.log("Calling function 1"); +impeeduino.functionCall(1, "This is the argument", function(value) { + server.log("Function 1 returned with value: " + value); +}); +``` + +## Licence + +The Impeeduino library is provided under the [MIT License](./LICENSE). \ No newline at end of file diff --git a/hardware/impeeduino/arduino/impeeduino/README.md b/hardware/impeeduino/arduino/impeeduino/README.md new file mode 100644 index 0000000..51021e6 --- /dev/null +++ b/hardware/impeeduino/arduino/impeeduino/README.md @@ -0,0 +1,81 @@ +# Impeeduino Communication Scheme +This document provides documentation for the communication scheme between the imp001 and the ATMega. Incoming serial data is interpreted byte by byte, with commands differentiated from regular ASCII data by checking the most significant bit of each byte. This allows us to take advantage of regular strings sent via Serial.print() on the Arduino as a simple way to pass data while still leaving sufficient address space to assign most common functions to concise commands. + +## Instruction Format +Instruction format: `1[op (3b)][addr (4b)]` + +Any byte received that does not have its MSB set to 1 is interpreted as ASCII data and is added to function call buffer. The next 3 bits (bits 6:4) define the opcode, which determines what operation to perform. The last 4 bits (bits 3:0) define an address, usually used to describe a pin number. + +### 000 (0x80): Configure Pin +A command to configure a pin must be followed by arbitrary data op specifying the config type. + +Arbitrary data command byte format: `1111[configtype (4b)]` + +| Config Type | Decimal | Hex | Binary | +| ----------- | ------- | --- | ------ | +| Input | 0 | 0x0 | 0000 | +| Input Pullup| 1 | 0x1 | 0001 | +| Output | 2 | 0x2 | 0010 | +| PWM Output | 3 | 0x3 | 0011 | + +### 001 (0x90): Digital Read +The Arduino responds to a digital read command with digital write command byte corresponding to the read value. For instance, if the Arduino receives `0x96`, instructing a digital read of pin 6, it will respond with `0xA6` (Digital Write 0 on Pin 6) if pin 6 is low or `0xB6` (Digital Write 1 on Pin 6) if pin 6 is high. + +### 010 (0xA0): Digital Write 0 +Write digital low to the specified pin address. +### 011 (0xB0): Digital Write 1 +Write digital high to the specified pin address. + +### 100 (0xC0): Analog Op +This opcode represents both analog reads and writes. Since there are only 5 analog inputs and 6 PWM enabled outputs on the Arduino, this first bit of the address field can be used to choose between a analogWrite or analogRead while still being able to address all the applicable pins. In the future it might be worth redesignating one of the function call opcodes in order to address boards with more outputs. + +Analog operation format: `1100[W/~R (1b)][addr (3b)]` + +For analog writes, the Arduino pin numbers are remapped to the available address space as shown below: + +| Arduino Pin Number | Command Address | Example Analog Write | +| -------- | --------- | ----------- | +| 3 | 0 | `0xC8 = 1100 1 000` | +| 5 | 1 | `0xC9 = 1100 1 001` | +| 6 | 2 | `0xCA = 1100 1 010` | +| 9 | 3 | `0xCB = 1100 1 011` | +| 10 | 4 | `0xCC = 1100 1 100` | +| 11 | 5 | `0xCD = 1100 1 101` | + +Analog writes must also pass a 1 byte argument for the PWM duty cycle. The two bytes after the intial analogWrite command must be arbitrary data bytes with the least significant word, then most significant word, of the duty cycle. +Thus a complete analogWrite instruction would be: `1100 1[addr (3b)], 1101[LSB (4b)], 1101[MSB (4b)]`. + +Analog reads return a 10-bit ADC value. The Arduino responds with a copy of the original analog operation command followed by 3 arbitrary data commands containing the 10-bit ADC value split into 3 words. The words are sent in little-endian order. +A complete analogRead response would be: `1100 0[addr (3b)], 1101 [ADC(3:0)], 1101 [ADC(7:4)], 1101 00[ADC(9:8)]` + +### 101 (0xD0): Arbitrary Data +This operation is used by other operations to send words of binary data. It is preferable to have an arbitrary data operation in order to avoid issues with interfering with ASCII data being sent as part of a function call or have awkward situations such as long strings of `0xFF` bytes being confused with a -1 read return value. + +### 110 (0xE0) and 111 (0xF0): Call/Return +These operations call user-defined functions in the Arduino code. A character array buffer stores incoming ASCII characters received on the UART bus and is passed as the argument to function calls. Between the opcodes `0xE0` and `0xF0`, there are 32 possible functions that may be called. These are referred to with a 5-bit ID, meaning that it may be more accurate to describe the call operation format as: `11[function id (5b)]` + +There are two function calls that are reserved for system use. + - *call 0* (`0xE0` or `11000000`) is used to clear the receive buffers. + - *call 31* (`0xFF` or `11111111`) may not be used to avoid any confusion with -1, which is commonly used to indicate "no data" by UART read functions. + +The complete function call process is summarized below: + +Valid functions ids are 1-30 (`0x01` to `0x1E`). +function 0 `(0x00)` is reserved as "clear buffer." +function 31 `(0xFF)` is not allowed to avoid confusion with -1. + +1. Imp sends *call 0x00* to clear Arduino function buffer +2. Imp sends function argument as ASCII characters (0-127) +3. Arduino places received characters into buffer +4. Imp sends *call 0xXX* to initiate call of the function with id number 0xXX +5. Arduino calls *functionXX()* with the function buffer's contents as the argument +6. *functionXX* returns, optionally with a character array return value +7. Arduino sends *call 0x00* to clear Imp's return value buffer +8. Arduino sends return value as ASCII characters +9. Imp places received characters into buffer +10. Arduino sends *call 0xXX* to indicate function return +11. If a callback has been set, Imp calls it with the returned value as an argument. + +## License + +The Impeeduino Arduino sketch and associated materials are provided under the [MIT License](../../LICENSE). \ No newline at end of file diff --git a/hardware/impeeduino/arduino/impeeduino/impeeduino.ino b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino new file mode 100644 index 0000000..382bca0 --- /dev/null +++ b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino @@ -0,0 +1,195 @@ +// Copyright (c) 2016 Electric Imp +// This file is licensed under the MIT License +// http://opensource.org/licenses/MIT + +#define VERSION "1.0.0" + +#define BAUD_RATE 115200 +#define DELAY_WRITE 50 + +#define MASK_OP 0xF0 +#define OP_CONFIGURE 0x80 +#define OP_DIGITAL_READ 0x90 +#define OP_DIGITAL_WRITE_0 0xA0 +#define OP_DIGITAL_WRITE_1 0xB0 +#define OP_ANALOG 0xC0 +#define OP_ARB 0xD0 +#define OP_CALL0 0xE0 +#define OP_CALL1 0xF0 + +#define MASK_CONFIG 0x0F +#define CONFIG_INPUT 0x00 +#define CONFIG_INPUT_PULLUP 0x01 +#define CONFIG_OUTPUT 0x02 +#define CONFIG_OUTPUT_PWM 0x03 + +#define MASK_DIGITAL_ADDR 0x0F +#define MASK_DIGITAL_WRITE 0x10 +#define MASK_ANALOG_W 0x08 +#define MASK_ANALOG_ADDR 0x07 +#define MASK_CALL 0x1F + +const int PWM_PINMAP[6] = {3, 5, 6, 9, 10, 11}; + +unsigned int rxByte = 0; +unsigned int rxOp = 0; +char rxbuffer[1024]; +int rxbufferindex = 0; + +// ========== USER-DEFINED FUNCTIONS ========== // +char* function01(char* buf) { + // Send back data from the Arduino + Serial.print(millis()); + return ""; +} +char* function02(char* buf) { return buf; } +char* function03(char* buf) { return buf; } +char* function04(char* buf) { return buf; } +char* function05(char* buf) { return buf; } +char* function06(char* buf) { return buf; } +char* function07(char* buf) { return buf; } +char* function08(char* buf) { return buf; } +char* function09(char* buf) { return buf; } +char* function0A(char* buf) { return buf; } +char* function0B(char* buf) { return buf; } +char* function0C(char* buf) { return buf; } +char* function0D(char* buf) { return buf; } +char* function0E(char* buf) { return buf; } +char* function0F(char* buf) { return buf; } + +char* function10(char* buf) { return buf; } +char* function11(char* buf) { return buf; } +char* function12(char* buf) { return buf; } +char* function13(char* buf) { return buf; } +char* function14(char* buf) { return buf; } +char* function15(char* buf) { return buf; } +char* function16(char* buf) { return buf; } +char* function17(char* buf) { return buf; } +char* function18(char* buf) { return buf; } +char* function19(char* buf) { return buf; } +char* function1A(char* buf) { return buf; } +char* function1B(char* buf) { return buf; } +char* function1C(char* buf) { return buf; } +char* function1D(char* buf) { return buf; } +char* function1E(char* buf) { return buf; } + +void setup() { + Serial.begin(BAUD_RATE); + Serial.print("Impeeduino Version: "); + Serial.println(VERSION); + Serial.write(OP_CALL0); + Serial.flush(); + digitalWrite(7, HIGH); + delay(500); + digitalWrite(7,LOW); +} + +void loop() { + if (Serial.available()) { + // get the new byte: + + rxByte = (char)Serial.read(); + if (rxByte & 0x80) { + // Not ASCII text, attempt to decode opcode + rxOp = rxByte & MASK_OP; + if (rxOp == OP_DIGITAL_READ) { + if (digitalRead(rxByte & MASK_DIGITAL_ADDR) == HIGH) { + //Serial.println("Digital HIGH"); + Serial.write((rxByte & MASK_DIGITAL_ADDR) | OP_DIGITAL_WRITE_1); + } else { + //Serial.println("Digital LOW"); + Serial.write((rxByte & MASK_DIGITAL_ADDR) | OP_DIGITAL_WRITE_0); + } + } else if (rxOp == OP_DIGITAL_WRITE_0) { + //Serial.println("Writing LOW"); + digitalWrite(rxByte & MASK_DIGITAL_ADDR, LOW); + delay(DELAY_WRITE); + } else if (rxOp == OP_DIGITAL_WRITE_1) { + //Serial.println("Writing HIGH"); + digitalWrite(rxByte & MASK_DIGITAL_ADDR, HIGH); + delay(DELAY_WRITE); + } else if (rxOp == OP_ANALOG) { + if (rxByte & MASK_ANALOG_W) { + int addr = rxByte & MASK_ANALOG_ADDR; + // Wait for value bytes to arrive + while(Serial.available() < 2); + // Lowest order bits (3-0) + char value = Serial.read() & 0x0F; + // Higest order bits (7-4) + value = value | ((Serial.read() & 0x0F) << 4); + //Serial.write(value); + analogWrite(PWM_PINMAP[addr], value); + } else { + Serial.write(rxByte); + int analogvalue = analogRead(rxByte & MASK_ANALOG_ADDR); + // Lowest order bits (3-0) + Serial.write(OP_ARB | (analogvalue & 0x0F)); + // Middle bits (7-4) + Serial.write(OP_ARB | ((analogvalue >> 4) & 0x0F)); + // Highest order bits (9-8) + Serial.write(OP_ARB | ((analogvalue >> 8) & 0x0F)); + } + + } else if (rxOp == OP_CONFIGURE) { + switch (Serial.read() & MASK_CONFIG) { + case CONFIG_INPUT: + pinMode(rxByte & MASK_DIGITAL_ADDR, INPUT); + break; + case CONFIG_INPUT_PULLUP: + pinMode(rxByte & MASK_DIGITAL_ADDR, INPUT_PULLUP); + break; + case CONFIG_OUTPUT: + pinMode(rxByte & MASK_DIGITAL_ADDR, OUTPUT); + break; + } + } else if (rxOp == OP_ARB) { + + } else { + // Call Function Op: 111X + rxbuffer[rxbufferindex] = '\0'; + switch (rxByte & MASK_CALL) { + case 0x00: Serial.write(rxbuffer); break; + case 0x01: Serial.write(function01(rxbuffer)); break; + case 0x02: Serial.write(function02(rxbuffer)); break; + case 0x03: Serial.write(function03(rxbuffer)); break; + case 0x04: Serial.write(function04(rxbuffer)); break; + case 0x05: Serial.write(function05(rxbuffer)); break; + case 0x06: Serial.write(function06(rxbuffer)); break; + case 0x07: Serial.write(function07(rxbuffer)); break; + case 0x08: Serial.write(function08(rxbuffer)); break; + case 0x09: Serial.write(function09(rxbuffer)); break; + case 0x0A: Serial.write(function0A(rxbuffer)); break; + case 0x0B: Serial.write(function0B(rxbuffer)); break; + case 0x0C: Serial.write(function0C(rxbuffer)); break; + case 0x0D: Serial.write(function0D(rxbuffer)); break; + case 0x0E: Serial.write(function0E(rxbuffer)); break; + case 0x0F: Serial.write(function0F(rxbuffer)); break; + + case 0x10: Serial.write(function10(rxbuffer)); break; + case 0x11: Serial.write(function11(rxbuffer)); break; + case 0x12: Serial.write(function12(rxbuffer)); break; + case 0x13: Serial.write(function13(rxbuffer)); break; + case 0x14: Serial.write(function14(rxbuffer)); break; + case 0x15: Serial.write(function15(rxbuffer)); break; + case 0x16: Serial.write(function16(rxbuffer)); break; + case 0x17: Serial.write(function17(rxbuffer)); break; + case 0x18: Serial.write(function18(rxbuffer)); break; + case 0x19: Serial.write(function19(rxbuffer)); break; + case 0x1A: Serial.write(function1A(rxbuffer)); break; + case 0x1B: Serial.write(function1B(rxbuffer)); break; + case 0x1C: Serial.write(function1C(rxbuffer)); break; + case 0x1D: Serial.write(function1D(rxbuffer)); break; + case 0x1E: Serial.write(function1E(rxbuffer)); break; + } + Serial.write(rxByte); + Serial.flush(); + rxbufferindex = 0; + } + } else { + // Received ASCII text, insert into rxbuffer + rxbuffer[rxbufferindex] = char(rxByte); + rxbufferindex++; + } + } +} + diff --git a/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.standard.hex b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.standard.hex new file mode 100644 index 0000000..c476b68 --- /dev/null +++ b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.standard.hex @@ -0,0 +1,193 @@ +:100000000C9481000C94A9000C94A9000C94A900F4 +:100010000C94A9000C94A9000C94A9000C94A900BC +:100020000C94A9000C94A9000C94A9000C94A900AC +:100030000C94A9000C94A9000C94A9000C94A9009C +:100040000C940C030C94A9000C94DA020C94B402E6 +:100050000C94A9000C94A9000C94A9000C94A9007C +:100060000C94A9000C94A9005405300554055405BE +:1000700054055405540554055405540554055405B8 +:1000800054055405540554055405540554055405A8 +:100090005405540554055405540554055405540598 +:1000A00054055405540500000000240027002A00D0 +:1000B00000000000250028002B00000000002300A5 +:1000C00026002900040404040404040402020202B9 +:1000D000020203030303030301020408102040800B +:1000E000010204081020010204081020000000088A +:1000F00000020100000304070000000000000000EF +:10010000830511241FBECFEFD8E0DEBFCDBF11E0C5 +:10011000A0E0B1E0EEEBFBE002C005900D92AA3347 +:10012000B107D9F725E0AAE3B1E001C01D92A63ED0 +:10013000B207E1F710E0C1E8D0E004C02197FE016A +:100140000E94D205C038D107C9F70E9456030C940B +:10015000DD050C940000CF92DF92EF92FF920F9397 +:100160001F93CF93DF936C017A018B01C0E0D0E045 +:10017000CE15DF0589F0D8016D918D01D601ED9185 +:10018000FC910190F081E02DC6010995892B11F4B5 +:100190007E0102C02196ECCFC701DF91CF911F9164 +:1001A0000F91FF90EF90DF90CF900895FC01918D1B +:1001B000828D981761F0828DDF01A80FB11D5D96C9 +:1001C0008C91928D9F5F9F73928F90E008958FEF37 +:1001D0009FEF0895FC01918D828D981731F0828DEB +:1001E000E80FF11D858D90E008958FEF9FEF089542 +:1001F000FC01918D228D892F90E0805C9F4F821BA6 +:1002000091098F739927089589E495E00E94F80079 +:1002100021E0892B09F420E0822F0895FC01848DD0 +:10022000DF01A80FB11DA35ABF4F2C91848D90E020 +:1002300001968F739927848FA689B7892C93A089FB +:10024000B1898C9180648C93938D848D981306C0B2 +:100250000288F389E02D80818F7D80830895EF925D +:10026000FF920F931F93CF93DF93EC0181E0888F70 +:100270009B8D8C8D981305C0E889F989808185FD57 +:1002800024C0F62E0B8D10E00F5F1F4F0F73112748 +:10029000E02E8C8DE8120CC00FB607FCFACFE8896F +:1002A000F989808185FFF5CFCE010E940E01F1CF43 +:1002B0008B8DFE01E80FF11DE35AFF4FF0820B8F8B +:1002C000EA89FB898081806207C0EE89FF896083AB +:1002D000E889F98980818064808381E090E0DF9102 +:1002E000CF911F910F91FF90EF900895682F89E4AF +:1002F00095E00C942F01682F89E495E00C942F0170 +:10030000CF93DF93EC01888D8823C9F0EA89FB89BC +:10031000808185FD05C0A889B9898C9186FD0FC0B3 +:100320000FB607FCF5CF808185FFF2CFA889B98988 +:100330008C9185FFEDCFCE010E940E01E7CFDF91BA +:10034000CF91089580E090E0892B29F00E9404016C +:1003500081110C9400000895833081F028F48130DD +:1003600099F08230A1F008958730A9F08830B9F073 +:100370008430D1F4809180008F7D03C08091800013 +:100380008F7780938000089584B58F7702C084B5FD +:100390008F7D84BD08958091B0008F7703C08091D8 +:1003A000B0008F7D8093B00008951F93CF93DF93AB +:1003B000282F30E0F901E451FF4F8491F901E85210 +:1003C000FF4FD491F901EC53FF4FC491CC23C9F0F6 +:1003D000162F81110E94AC01EC2FF0E0EE0FFF1FF1 +:1003E000E055FF4FA591B4918FB7F894111105C056 +:1003F0009C91ED2FE095E92302C0EC91ED2BEC935D +:100400008FBFDF91CF911F910895CF93DF9390E03D +:10041000FC01E852FF4F2491FC01EC53FF4F849103 +:10042000882361F190E0880F991FFC01EA55FF4F86 +:10043000C591D491FC01E055FF4FA591B491611194 +:1004400009C09FB7F8948881209582238883EC9116 +:100450002E230BC0623061F49FB7F8943881822F4D +:10046000809583238883EC912E2B2C939FBF06C00D +:100470008FB7F894E8812E2B28838FBFDF91CF911F +:1004800008953FB7F89480913B0190913C01A09171 +:100490003D01B0913E0126B5A89B05C02F3F19F044 +:1004A0000196A11DB11D3FBFBA2FA92F982F8827F4 +:1004B000820F911DA11DB11DBC01CD0142E0660F4F +:1004C000771F881F991F4A95D1F708958F929F92A1 +:1004D000AF92BF92CF92DF92EF92FF926B017C01BD +:1004E0000E9441024B015C01C114D104E104F104FA +:1004F000F1F00E944102DC01CB0188199909AA0997 +:10050000BB09883E9340A105B10570F321E0C21AF2 +:10051000D108E108F10888EE880E83E0981EA11C3E +:10052000B11CC114D104E104F10419F7DDCFFF902F +:10053000EF90DF90CF90BF90AF909F908F900895F5 +:10054000009769F0FC0101900020E9F73197AF01B5 +:10055000481B590BBC0189E495E00C94AB0080E08A +:1005600090E008950E94D8051F920F920FB60F9247 +:1005700011242F933F934F935F936F937F938F93A8 +:100580009F93AF93BF93EF93FF9389E495E00E940D +:100590000E01FF91EF91BF91AF919F918F917F914C +:1005A0006F915F914F913F912F910F900FBE0F90E0 +:1005B0001F9018951F920F920FB60F9211242F9330 +:1005C0008F939F93EF93FF93E0915905F0915A0514 +:1005D0008081E0915F05F091600582FD12C09081FD +:1005E000809162058F5F8F7320916305821751F0B0 +:1005F000E0916205F0E0E75BFA4F958F809362052A +:1006000001C08081FF91EF919F918F912F910F9069 +:100610000FBE0F901F9018951F920F920FB60F925A +:1006200011242F933F938F939F93AF93BF93809108 +:100630003F0190914001A0914101B0914201309160 +:100640003A0123E0230F2D3720F40196A11DB11D9F +:1006500005C026E8230F0296A11DB11D20933A0183 +:1006600080933F0190934001A0934101B0934201D8 +:1006700080913B0190913C01A0913D01B0913E01E0 +:100680000196A11DB11D80933B0190933C01A09365 +:100690003D01B0933E01BF91AF919F918F913F91EA +:1006A0002F910F900FBE0F901F901895CF93DF934F +:1006B000CDB7DEB7A1970FB6F894DEBF0FBECDBFA2 +:1006C000789484B5826084BD84B5816084BD85B52D +:1006D000826085BD85B5816085BD80916E00816039 +:1006E00080936E00109281008091810082608093DF +:1006F00081008091810081608093810080918000E1 +:100700008160809380008091B10084608093B1000B +:100710008091B00081608093B00080917A00846005 +:1007200080937A0080917A00826080937A00809131 +:100730007A00816080937A0080917A00806880934B +:100740007A001092C100E0915905F0915A0582E0BB +:100750008083E0915505F09156051082E091570590 +:10076000F091580580E1808310926105E0915D056C +:10077000F0915E0586E08083E0915B05F0915C0579 +:10078000808180618083E0915B05F0915C058081D0 +:1007900088608083E0915B05F0915C0580818068D2 +:1007A0008083E0915B05F0915C0580818F7D808383 +:1007B0008CE191E00E94A00281E391E00E94A002FE +:1007C00087E391E00E94A00280EE90E00E947B010E +:1007D00089E495E00E94800161E087E00E94D501F4 +:1007E00064EF71E080E090E00E94660260E087E0E4 +:1007F0000E94D5017AE0C72ED12CE12CF12C89E49E +:1008000095E00E94F800892B09F46EC189E495E017 +:100810000E94D600182F082E000C990B90934805C3 +:100820008093470587FF53C19C01207F3327309376 +:100830004605209345052039310511F51F70412FDC +:1008400050E0FA01E451FF4F8491FA01E852FF4F62 +:10085000B490FA01EC53FF4F0491002309F447C10F +:1008600081110E94AC01E02FF0E0EE0FFF1FE65473 +:10087000FF4FA591B491EC91EB2109F438C13CC133 +:10088000203A310511F460E004C0203B310561F4E9 +:1008900061E0812F8F700E94D50162E370E080E0FB +:1008A00090E00E94660220C1203C310509F0A5C0FD +:1008B000012F077013FF72C0A02E000FBB0889E440 +:1008C00095E00E94F8000297D4F389E495E00E9435 +:1008D000D600182F1F7089E495E00E94D60044E0EE +:1008E000880F991F4A95E1F7182BF501EE0FFF1FAE +:1008F000E05FFE4FA080B18061E08A2D0E9405027A +:10090000112309F446C0F501FF27E451FF4FE4919C +:10091000E33011F148F4E130D1F0E230D1F584B5A3 +:10092000806284BD18BDE0C0E73019F1E83049F1BC +:10093000E43079F580918000806280938000812F7F +:10094000110F990B90938B0080938A00CDC084B5D2 +:10095000806884BD17BDC8C0809180008068809386 +:100960008000812F110F990B90938900809388004C +:10097000BBC08091B00080688093B0001093B3003A +:10098000B3C08091B00080628093B0001093B40037 +:10099000ABC060E08A2D0E94D501A6C00E947601FE +:1009A000006400937C0080917A00806480937A00D8 +:1009B00080917A0086FDFCCF0091780080917900CB +:1009C00010E0182BC8018F709927806D0E947B0161 +:1009D000C80124E0959587952A95E1F78F709927AE +:1009E000806D0E947B01812F992787FD9A958F70DA +:1009F0009927806D0E947B0177C020383105C9F4AA +:100A000089E495E00E94D6008F7099278130910586 +:100A100041F08230910539F0892B09F065C060E022 +:100A200003C062E001C061E0812F8F700E94050267 +:100A30005BC0203D310509F457C0E0914305F091BA +:100A40004405ED5BFE4F1082E12FEF710E2E000C7E +:100A5000FF0BEF31F10560F5EC5CFF4F0C94D20514 +:100A60008FB7F89420913F013091400140914101AE +:100A7000509142018FBF19A28E010F5D1F4FCA0115 +:100A8000B901A70196010E94B005605DF80162936B +:100A90008F01211531054105510589F7CF010E94CC +:100AA000A00289E391E002C083E491E00E94A002E9 +:100AB00080914705909148050E94760189E495E070 +:100AC0000E94800110924405109243050DC0809150 +:100AD000430590914405FC01ED5BFE4F10830196A8 +:100AE00090934405809343050E94A20188CE812FF4 +:100AF000110F990B806A04C0812F110F990B806B25 +:100B00000E947601F1CFE9E4F5E01382128288EECB +:100B100093E0A0E0B0E084839583A683B78384E06C +:100B200091E09183808385EC90E09587848784ECC5 +:100B300090E09787868780EC90E0918B808B81ECAA +:100B400090E0938B828B82EC90E0958B848B86EC8B +:100B500090E0978B868B118E128E138E148E0895D3 +:100B6000A1E21A2EAA1BBB1BFD010DC0AA1FBB1FB1 +:100B7000EE1FFF1FA217B307E407F50720F0A21B23 +:100B8000B30BE40BF50B661F771F881F991F1A9490 +:100B900069F760957095809590959B01AC01BD01BA +:100BA000CF010895EE0FFF1F0590F491E02D0994F9 +:0E0BB00081E090E0F8940C94DD05F894FFCFFE +:100BBE00000000002F01AB00F800D600EA00800113 +:100BCE0003000500060009000A000B00496D706560 +:100BDE00656475696E6F2056657273696F6E3A2023 +:0A0BEE0000312E302E30000D0A00F9 +:00000001FF diff --git a/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.with_bootloader.standard.hex b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.with_bootloader.standard.hex new file mode 100644 index 0000000..60059bf --- /dev/null +++ b/hardware/impeeduino/arduino/impeeduino/impeeduino.ino.with_bootloader.standard.hex @@ -0,0 +1,227 @@ +:100000000C9481000C94A9000C94A9000C94A900F4 +:100010000C94A9000C94A9000C94A9000C94A900BC +:100020000C94A9000C94A9000C94A9000C94A900AC +:100030000C94A9000C94A9000C94A9000C94A9009C +:100040000C940C030C94A9000C94DA020C94B402E6 +:100050000C94A9000C94A9000C94A9000C94A9007C +:100060000C94A9000C94A9005405300554055405BE +:1000700054055405540554055405540554055405B8 +:1000800054055405540554055405540554055405A8 +:100090005405540554055405540554055405540598 +:1000A00054055405540500000000240027002A00D0 +:1000B00000000000250028002B00000000002300A5 +:1000C00026002900040404040404040402020202B9 +:1000D000020203030303030301020408102040800B +:1000E000010204081020010204081020000000088A +:1000F00000020100000304070000000000000000EF +:10010000830511241FBECFEFD8E0DEBFCDBF11E0C5 +:10011000A0E0B1E0EEEBFBE002C005900D92AA3347 +:10012000B107D9F725E0AAE3B1E001C01D92A63ED0 +:10013000B207E1F710E0C1E8D0E004C02197FE016A +:100140000E94D205C038D107C9F70E9456030C940B +:10015000DD050C940000CF92DF92EF92FF920F9397 +:100160001F93CF93DF936C017A018B01C0E0D0E045 +:10017000CE15DF0589F0D8016D918D01D601ED9185 +:10018000FC910190F081E02DC6010995892B11F4B5 +:100190007E0102C02196ECCFC701DF91CF911F9164 +:1001A0000F91FF90EF90DF90CF900895FC01918D1B +:1001B000828D981761F0828DDF01A80FB11D5D96C9 +:1001C0008C91928D9F5F9F73928F90E008958FEF37 +:1001D0009FEF0895FC01918D828D981731F0828DEB +:1001E000E80FF11D858D90E008958FEF9FEF089542 +:1001F000FC01918D228D892F90E0805C9F4F821BA6 +:1002000091098F739927089589E495E00E94F80079 +:1002100021E0892B09F420E0822F0895FC01848DD0 +:10022000DF01A80FB11DA35ABF4F2C91848D90E020 +:1002300001968F739927848FA689B7892C93A089FB +:10024000B1898C9180648C93938D848D981306C0B2 +:100250000288F389E02D80818F7D80830895EF925D +:10026000FF920F931F93CF93DF93EC0181E0888F70 +:100270009B8D8C8D981305C0E889F989808185FD57 +:1002800024C0F62E0B8D10E00F5F1F4F0F73112748 +:10029000E02E8C8DE8120CC00FB607FCFACFE8896F +:1002A000F989808185FFF5CFCE010E940E01F1CF43 +:1002B0008B8DFE01E80FF11DE35AFF4FF0820B8F8B +:1002C000EA89FB898081806207C0EE89FF896083AB +:1002D000E889F98980818064808381E090E0DF9102 +:1002E000CF911F910F91FF90EF900895682F89E4AF +:1002F00095E00C942F01682F89E495E00C942F0170 +:10030000CF93DF93EC01888D8823C9F0EA89FB89BC +:10031000808185FD05C0A889B9898C9186FD0FC0B3 +:100320000FB607FCF5CF808185FFF2CFA889B98988 +:100330008C9185FFEDCFCE010E940E01E7CFDF91BA +:10034000CF91089580E090E0892B29F00E9404016C +:1003500081110C9400000895833081F028F48130DD +:1003600099F08230A1F008958730A9F08830B9F073 +:100370008430D1F4809180008F7D03C08091800013 +:100380008F7780938000089584B58F7702C084B5FD +:100390008F7D84BD08958091B0008F7703C08091D8 +:1003A000B0008F7D8093B00008951F93CF93DF93AB +:1003B000282F30E0F901E451FF4F8491F901E85210 +:1003C000FF4FD491F901EC53FF4FC491CC23C9F0F6 +:1003D000162F81110E94AC01EC2FF0E0EE0FFF1FF1 +:1003E000E055FF4FA591B4918FB7F894111105C056 +:1003F0009C91ED2FE095E92302C0EC91ED2BEC935D +:100400008FBFDF91CF911F910895CF93DF9390E03D +:10041000FC01E852FF4F2491FC01EC53FF4F849103 +:10042000882361F190E0880F991FFC01EA55FF4F86 +:10043000C591D491FC01E055FF4FA591B491611194 +:1004400009C09FB7F8948881209582238883EC9116 +:100450002E230BC0623061F49FB7F8943881822F4D +:10046000809583238883EC912E2B2C939FBF06C00D +:100470008FB7F894E8812E2B28838FBFDF91CF911F +:1004800008953FB7F89480913B0190913C01A09171 +:100490003D01B0913E0126B5A89B05C02F3F19F044 +:1004A0000196A11DB11D3FBFBA2FA92F982F8827F4 +:1004B000820F911DA11DB11DBC01CD0142E0660F4F +:1004C000771F881F991F4A95D1F708958F929F92A1 +:1004D000AF92BF92CF92DF92EF92FF926B017C01BD +:1004E0000E9441024B015C01C114D104E104F104FA +:1004F000F1F00E944102DC01CB0188199909AA0997 +:10050000BB09883E9340A105B10570F321E0C21AF2 +:10051000D108E108F10888EE880E83E0981EA11C3E +:10052000B11CC114D104E104F10419F7DDCFFF902F +:10053000EF90DF90CF90BF90AF909F908F900895F5 +:10054000009769F0FC0101900020E9F73197AF01B5 +:10055000481B590BBC0189E495E00C94AB0080E08A +:1005600090E008950E94D8051F920F920FB60F9247 +:1005700011242F933F934F935F936F937F938F93A8 +:100580009F93AF93BF93EF93FF9389E495E00E940D +:100590000E01FF91EF91BF91AF919F918F917F914C +:1005A0006F915F914F913F912F910F900FBE0F90E0 +:1005B0001F9018951F920F920FB60F9211242F9330 +:1005C0008F939F93EF93FF93E0915905F0915A0514 +:1005D0008081E0915F05F091600582FD12C09081FD +:1005E000809162058F5F8F7320916305821751F0B0 +:1005F000E0916205F0E0E75BFA4F958F809362052A +:1006000001C08081FF91EF919F918F912F910F9069 +:100610000FBE0F901F9018951F920F920FB60F925A +:1006200011242F933F938F939F93AF93BF93809108 +:100630003F0190914001A0914101B0914201309160 +:100640003A0123E0230F2D3720F40196A11DB11D9F +:1006500005C026E8230F0296A11DB11D20933A0183 +:1006600080933F0190934001A0934101B0934201D8 +:1006700080913B0190913C01A0913D01B0913E01E0 +:100680000196A11DB11D80933B0190933C01A09365 +:100690003D01B0933E01BF91AF919F918F913F91EA +:1006A0002F910F900FBE0F901F901895CF93DF934F +:1006B000CDB7DEB7A1970FB6F894DEBF0FBECDBFA2 +:1006C000789484B5826084BD84B5816084BD85B52D +:1006D000826085BD85B5816085BD80916E00816039 +:1006E00080936E00109281008091810082608093DF +:1006F00081008091810081608093810080918000E1 +:100700008160809380008091B10084608093B1000B +:100710008091B00081608093B00080917A00846005 +:1007200080937A0080917A00826080937A00809131 +:100730007A00816080937A0080917A00806880934B +:100740007A001092C100E0915905F0915A0582E0BB +:100750008083E0915505F09156051082E091570590 +:10076000F091580580E1808310926105E0915D056C +:10077000F0915E0586E08083E0915B05F0915C0579 +:10078000808180618083E0915B05F0915C058081D0 +:1007900088608083E0915B05F0915C0580818068D2 +:1007A0008083E0915B05F0915C0580818F7D808383 +:1007B0008CE191E00E94A00281E391E00E94A002FE +:1007C00087E391E00E94A00280EE90E00E947B010E +:1007D00089E495E00E94800161E087E00E94D501F4 +:1007E00064EF71E080E090E00E94660260E087E0E4 +:1007F0000E94D5017AE0C72ED12CE12CF12C89E49E +:1008000095E00E94F800892B09F46EC189E495E017 +:100810000E94D600182F082E000C990B90934805C3 +:100820008093470587FF53C19C01207F3327309376 +:100830004605209345052039310511F51F70412FDC +:1008400050E0FA01E451FF4F8491FA01E852FF4F62 +:10085000B490FA01EC53FF4F0491002309F447C10F +:1008600081110E94AC01E02FF0E0EE0FFF1FE65473 +:10087000FF4FA591B491EC91EB2109F438C13CC133 +:10088000203A310511F460E004C0203B310561F4E9 +:1008900061E0812F8F700E94D50162E370E080E0FB +:1008A00090E00E94660220C1203C310509F0A5C0FD +:1008B000012F077013FF72C0A02E000FBB0889E440 +:1008C00095E00E94F8000297D4F389E495E00E9435 +:1008D000D600182F1F7089E495E00E94D60044E0EE +:1008E000880F991F4A95E1F7182BF501EE0FFF1FAE +:1008F000E05FFE4FA080B18061E08A2D0E9405027A +:10090000112309F446C0F501FF27E451FF4FE4919C +:10091000E33011F148F4E130D1F0E230D1F584B5A3 +:10092000806284BD18BDE0C0E73019F1E83049F1BC +:10093000E43079F580918000806280938000812F7F +:10094000110F990B90938B0080938A00CDC084B5D2 +:10095000806884BD17BDC8C0809180008068809386 +:100960008000812F110F990B90938900809388004C +:10097000BBC08091B00080688093B0001093B3003A +:10098000B3C08091B00080628093B0001093B40037 +:10099000ABC060E08A2D0E94D501A6C00E947601FE +:1009A000006400937C0080917A00806480937A00D8 +:1009B00080917A0086FDFCCF0091780080917900CB +:1009C00010E0182BC8018F709927806D0E947B0161 +:1009D000C80124E0959587952A95E1F78F709927AE +:1009E000806D0E947B01812F992787FD9A958F70DA +:1009F0009927806D0E947B0177C020383105C9F4AA +:100A000089E495E00E94D6008F7099278130910586 +:100A100041F08230910539F0892B09F065C060E022 +:100A200003C062E001C061E0812F8F700E94050267 +:100A30005BC0203D310509F457C0E0914305F091BA +:100A40004405ED5BFE4F1082E12FEF710E2E000C7E +:100A5000FF0BEF31F10560F5EC5CFF4F0C94D20514 +:100A60008FB7F89420913F013091400140914101AE +:100A7000509142018FBF19A28E010F5D1F4FCA0115 +:100A8000B901A70196010E94B005605DF80162936B +:100A90008F01211531054105510589F7CF010E94CC +:100AA000A00289E391E002C083E491E00E94A002E9 +:100AB00080914705909148050E94760189E495E070 +:100AC0000E94800110924405109243050DC0809150 +:100AD000430590914405FC01ED5BFE4F10830196A8 +:100AE00090934405809343050E94A20188CE812FF4 +:100AF000110F990B806A04C0812F110F990B806B25 +:100B00000E947601F1CFE9E4F5E01382128288EECB +:100B100093E0A0E0B0E084839583A683B78384E06C +:100B200091E09183808385EC90E09587848784ECC5 +:100B300090E09787868780EC90E0918B808B81ECAA +:100B400090E0938B828B82EC90E0958B848B86EC8B +:100B500090E0978B868B118E128E138E148E0895D3 +:100B6000A1E21A2EAA1BBB1BFD010DC0AA1FBB1FB1 +:100B7000EE1FFF1FA217B307E407F50720F0A21B23 +:100B8000B30BE40BF50B661F771F881F991F1A9490 +:100B900069F760957095809590959B01AC01BD01BA +:100BA000CF010895EE0FFF1F0590F491E02D0994F9 +:0E0BB00081E090E0F8940C94DD05F894FFCFFE +:100BBE00000000002F01AB00F800D600EA00800113 +:100BCE0003000500060009000A000B00496D706560 +:100BDE00656475696E6F2056657273696F6E3A2023 +:0A0BEE0000312E302E30000D0A00F9 +:107E0000112484B714BE81FFF0D085E080938100F7 +:107E100082E08093C00088E18093C10086E0809377 +:107E2000C20080E18093C4008EE0C9D0259A86E02C +:107E300020E33CEF91E0309385002093840096BBD3 +:107E4000B09BFECF1D9AA8958150A9F7CC24DD24C4 +:107E500088248394B5E0AB2EA1E19A2EF3E0BF2EE7 +:107E6000A2D0813461F49FD0082FAFD0023811F036 +:107E7000013811F484E001C083E08DD089C08234E0 +:107E800011F484E103C0853419F485E0A6D080C0E4 +:107E9000853579F488D0E82EFF2485D0082F10E0AE +:107EA000102F00270E291F29000F111F8ED06801E7 +:107EB0006FC0863521F484E090D080E0DECF843638 +:107EC00009F040C070D06FD0082F6DD080E0C81688 +:107ED00080E7D80618F4F601B7BEE895C0E0D1E017 +:107EE00062D089930C17E1F7F0E0CF16F0E7DF06D8 +:107EF00018F0F601B7BEE89568D007B600FCFDCFD4 +:107F0000A601A0E0B1E02C9130E011968C91119780 +:107F100090E0982F8827822B932B1296FA010C0160 +:107F200087BEE89511244E5F5F4FF1E0A038BF0790 +:107F300051F7F601A7BEE89507B600FCFDCF97BE46 +:107F4000E89526C08437B1F42ED02DD0F82E2BD052 +:107F50003CD0F601EF2C8F010F5F1F4F84911BD097 +:107F6000EA94F801C1F70894C11CD11CFA94CF0C13 +:107F7000D11C0EC0853739F428D08EE10CD085E9AC +:107F80000AD08FE07ACF813511F488E018D01DD067 +:107F900080E101D065CF982F8091C00085FFFCCF94 +:107FA0009093C60008958091C00087FFFCCF809118 +:107FB000C00084FD01C0A8958091C6000895E0E648 +:107FC000F0E098E1908380830895EDDF803219F02E +:107FD00088E0F5DFFFCF84E1DECF1F93182FE3DFCA +:107FE0001150E9F7F2DF1F91089580E0E8DFEE27F6 +:047FF000FF270994CA +:027FFE00040479 +:0400000300007E007B +:00000001FF diff --git a/hardware/impeeduino/example/remotecontrol.agent.nut b/hardware/impeeduino/example/remotecontrol.agent.nut new file mode 100644 index 0000000..5e1dea3 --- /dev/null +++ b/hardware/impeeduino/example/remotecontrol.agent.nut @@ -0,0 +1,37 @@ +// Copyright (c) 2016 Electric Imp +// This file is licensed under the MIT License +// http://opensource.org/licenses/MIT + +function requestHandler(request, response) { + try { + if ("command" in request.query) { + local data = {}; + if ("async" in request.query && request.query.async == "true") { + data.async <- true; + } else { + data.async <- false; + } + if (request.query.command == "call") { + data.id <- request.query.id.tointeger(); + data.arg <- request.query.arg; + } else if (request.query.command == "analogWrite") { + data.pin <- request.query.pin.tointeger(); + data.val <- request.query.val.tofloat(); + if (data.val > 1.0) + data.val = data.val.tointeger(); + } else { + data.pin <- request.query.pin.tointeger(); + if ("val" in request.query) { + data.val <- request.query.val.tointeger(); + } + } + device.send(request.query.command, data); + } + response.send(200, "OK"); // "200: OK" is standard return message + } catch (ex) { + response.send(500, ("Agent Error: " + ex)); // Send 500 response if error occured + } +} + +// Register the callback function that will be triggered by incoming HTTP requests +http.onrequest(requestHandler); \ No newline at end of file diff --git a/hardware/impeeduino/example/remotecontrol.device.nut b/hardware/impeeduino/example/remotecontrol.device.nut new file mode 100644 index 0000000..0c289da --- /dev/null +++ b/hardware/impeeduino/example/remotecontrol.device.nut @@ -0,0 +1,58 @@ +// Copyright (c) 2016 Electric Imp +// This file is licensed under the MIT License +// http://opensource.org/licenses/MIT + +activityLED <- hardware.pin2; +linkLED <- hardware.pin8; + +server.log("Starting... "); +impeeduino <- Impeeduino(); + +agent.on("config", function(data) { + activityLED.write(1); + server.log("Configuring pin " + data.pin); + impeeduino.pinMode(data.pin, data.val); + activityLED.write(0); +}); +agent.on("digitalWrite", function(data) { + activityLED.write(1); + server.log("Writing " + data.val + " to pin " + data.pin); + impeeduino.digitalWrite(data.pin, data.val); + activityLED.write(0); +}); +agent.on("analogWrite", function(data) { + activityLED.write(1); + server.log("PWM " + data.val + " to pin " + data.pin); + impeeduino.analogWrite(data.pin, data.val); + activityLED.write(0); +}); +agent.on("digitalRead", function(data) { + activityLED.write(1); + if (data.async) { + impeeduino.digitalRead(data.pin, function(value) { + server.log("Async: Pin " + data.pin + " = " + value); + }); + } else { + server.log("Pin " + data.pin + " = " + impeeduino.digitalRead(data.pin)); + } + activityLED.write(0); +}); +agent.on("analogRead", function(data) { + activityLED.write(1); + if (data.async) { + impeeduino.analogRead(data.pin, function(value) { + server.log("Async: Pin A" + data.pin + " = " + value); + }); + } else { + server.log("Pin A" + data.pin + " = " + impeeduino.analogRead(data.pin)); + } + activityLED.write(0); +}); +agent.on("call", function(data) { + activityLED.write(1); + server.log("Calling function " + data.id); + impeeduino.functionCall(data.id, data.arg, function(value) { + server.log("Function " + data.id + " returned with value: " + value); + }); + activityLED.write(0); +}); \ No newline at end of file diff --git a/hardware/impeeduino/impeeduino.class.nut b/hardware/impeeduino/impeeduino.class.nut new file mode 100644 index 0000000..834551d --- /dev/null +++ b/hardware/impeeduino/impeeduino.class.nut @@ -0,0 +1,389 @@ +// Copyright (c) 2016 Electric Imp +// This file is licensed under the MIT License +// http://opensource.org/licenses/MIT + +class Impeeduino { + + static version = [1, 0, 0] + + static BAUD_RATE = 115200; + + // PWM enabled pins. -1: No PWM, otherwise gives address mapping + static PWM_PINMAP = [-1, -1, -1, 0, -1, 1, 2, -1, -1, 3, 4, 5, -1, -1]; + + static MASK_OP = 0xF0; + static OP_CONFIGURE = 0x80; + static OP_DIGITAL_READ = 0x90; + static OP_DIGITAL_WRITE_0 = 0xA0; + static OP_DIGITAL_WRITE_1 = 0xB0; + static OP_ANALOG = 0xC0; + static OP_ARB = 0xD0; + static OP_CALL = 0xE0; + + static MASK_CONFIG = 0x0F; + static CONFIG_INPUT = 0x00; + static CONFIG_INPUT_PULLUP = 0x01; + static CONFIG_OUTPUT = 0x02; + static CONFIG_OUTPUT_PWM = 0x03; + + static MASK_DIGITAL_ADDR = 0x0F; + static MASK_DIGITAL_WRITE = 0x10; + static MASK_ANALOG_W = 0x08; + static MASK_ANALOG_ADDR = 0x07; + static MASK_CALL = 0x1F; + + // -------------------- PRIVATE PROPERTIES -------------------- // + + _serial = null; // UART bus to communicate with AVR + _reset = null; // AVR reset pin + + _rxBuf = null; // Buffer for incoming data + _funcBuf = null; // Buffer for function return values + + _functioncb = null; // Table of function return callbacks + _digitalReadcb = null; // Table of digital read return callbacks + _analogReadcb = null; // Table of analog read return callbacks + + // -------------------- CONSTRUCTOR -------------------- // + /* + * The constructor takes two arguments to instantiate the class: a + * non-configured UART bus and a GPIO pin connected to the Arduino's reset + * line. These default to the configuration used on the Impeeduino rev2, + * using `uart57` for serial communications and `pin1` for the reset line. + */ + constructor(serial = hardware.uart57, reset = hardware.pin1) { + _serial = serial; + _serial.configure(BAUD_RATE, 8, PARITY_NONE, 1, NO_CTSRTS, _uartEvent.bindenv(this)); + + _reset = reset; + _reset.configure(DIGITAL_OUT); + + _funcBuf = blob(); + _rxBuf = blob(); + + _functioncb = {}; + //_functioncb[0] <- _versionCheck; + _digitalReadcb = {}; + _analogReadcb = {}; + + this.reset(); + } + + // -------------------- PUBLIC METHODS -------------------- // + /* + * Resets the ATMega processor by bouncing the reset pin. Note that reseting + * will block the imp for about 0.2 seconds. + */ + function reset() { + server.log("Resetting Duino...") + _reset.write(1); + imp.sleep(0.2); + _reset.write(0); + } + + /* + * Configures the specified GPIO pin to the specified mode. Possible + * configurations are DIGITAL_IN, DIGITAL_IN_PULLUP, DIGITAL_OUT, and PWM_OUT + */ + function pinMode(pin, mode) { + assert (typeof pin == "integer"); + assert (pin != 0 && pin != 1); // Do not reconfigure UART bus pins + _serial.write(OP_CONFIGURE | pin); + switch (mode) { + case DIGITAL_IN: + _serial.write(OP_ARB | CONFIG_INPUT); + break; + case DIGITAL_IN_PULLUP: + _serial.write(OP_ARB | CONFIG_INPUT_PULLUP); + break; + case DIGITAL_OUT: + _serial.write(OP_ARB | CONFIG_OUTPUT); + break; + case PWM_OUT: + assert (PWM_PINMAP[pin] != -1); + _serial.write(OP_ARB | CONFIG_OUTPUT_PWM); + break; + default: + server.error("Invalid pin mode: " + mode); + _serial.write(OP_ARB | CONFIG_INPUT); + break; + } + } + + /* + * Writes a value to the specified digital pin. Value can be either a + * boolean or an integer value. For boolean values, true corresponding to + * high and false to low. For integers, non-zero values correspond to high + * and zero corresponds to false. + */ + function digitalWrite(pin, value) { + assert (typeof pin == "integer"); + assert (typeof value == "integer" || typeof value == "bool"); + if (value) { + _serial.write(OP_DIGITAL_WRITE_1 | pin); + } else { + _serial.write(OP_DIGITAL_WRITE_0 | pin); + } + _serial.flush(); + } + + /* + * Writes an analog value (PWM wave) to a pin. *value* is an integer value + * representing the duty cycle and ranges between 0 (off) and 255 + * (always on). For compatibility with imp code, value may also be a + * floating point duty ratio from 0.0 to 1.0. This is then rounded to the + * nearest available value. + */ + function analogWrite(pin, value) { + assert (typeof pin == "integer"); + if (PWM_PINMAP[pin] == -1) throw "Pin " + pin + " does not have PWM capability"; + + local writeVal = 0; + if (typeof value == "integer") { + if (value < 0 || value > 255) throw "Integer analogWrite values must be between 0 and 255"; + writeVal = value; + } else if (typeof value == "float") { + if (value < 0.0 || value > 1.0) throw "Float analogWrite values must be between 0.0 and 1.0"; + writeVal = (value * 255).tointeger(); + } + + _serial.write(OP_ANALOG | MASK_ANALOG_W | PWM_PINMAP[pin]); + // Lowest order bits (3-0) + _serial.write(OP_ARB | (writeVal & 0x0000000F)); + // Higest order bits (7-4) + _serial.write(OP_ARB | ((writeVal & 0x000000F0) >> 4)); + _serial.flush(); + } + + /* + * Reads the logical value of the specified digital pin and returns it as + * an integer. A value of 0 corresponds to digital low, a value of 1 + * corresponds to digital high. + */ + function digitalRead(pin, cb = null) { + assert (typeof pin == "integer"); + _serial.write(OP_DIGITAL_READ | pin); + _serial.flush(); + + if (cb) { + _digitalReadcb[pin] <- cb; + } else { + local target_low = OP_DIGITAL_WRITE_0 | pin; // Search for ops with a digital write pattern and addr = pin + local target_high = OP_DIGITAL_WRITE_1 | pin; + local readByte = _serial.read(); + local timeout_count = 0; + while (readByte != target_low && readByte != target_high) { + // Save other data to buffer + if (readByte != -1) { + _rxBuf.seek(0, 'e'); + _rxBuf.writen(readByte, 'b'); + } + timeout_count++; + if (timeout_count > 200) { + //server.log("Read Timeout, retrying") + timeout_count = 0; + _serial.write(OP_DIGITAL_READ | pin); + _serial.flush(); + } + readByte = _serial.read(); + } + //server.log(format("0x%02X", readByte)); + imp.wakeup(0, _parseRXBuffer.bindenv(this)); + + return readByte & MASK_DIGITAL_WRITE ? 1 : 0; + } + } + + /* + * Reads the value of the specified analog pin and returns it as an integer. + * The Arduino has a 10-bit ADC, so returned values will range from 0 to 1023. + */ + function analogRead(pin, cb = null) { + assert (typeof pin == "integer"); + if (pin < 0 || pin > 5) throw "Invalid analog input number: " + pin; + _serial.write(OP_ANALOG | pin); + _serial.flush(); + imp.sleep(0.000015); + if (cb) { + _analogReadcb[pin] <- cb; + } else { + local target = OP_ANALOG | pin; + local readByte = 0; + local timeout_count = 0; + local value = blob(2); + // Wait for Arduino to send back result + do { + readByte = _serial.read(); + // Save other data to buffer + if (readByte != -1) { + _rxBuf.seek(0, 'e'); + _rxBuf.writen(readByte, 'b'); + } + timeout_count++; + if (timeout_count > 500) { + //server.log("Read Timeout, retrying") + timeout_count = 0; + _serial.write(OP_ANALOG | pin); + _serial.flush(); + } + } while (readByte != target) + + // Wait for 1st word (bits 3:0) + do { + readByte = _serial.read(); + // Save other data to buffer + if (readByte != -1) { + _rxBuf.seek(0, 'e'); + _rxBuf.writen(readByte, 'b'); + } + } while ((readByte & MASK_OP) != OP_ARB) + value[0] = readByte & 0x0F; + + // Wait for 2nd word (bits 7:4) + do { + readByte = _serial.read(); + // Save other data to buffer + if (readByte != -1) { + _rxBuf.seek(0, 'e'); + _rxBuf.writen(readByte, 'b'); + } + } while ((readByte & MASK_OP) != OP_ARB) + value[0] = value[0] | ((readByte & 0x0F) << 4); + + // Wait for 3rd word (bits 9:8) + do { + readByte = _serial.read(); + // Save other data to buffer + if (readByte != -1) { + _rxBuf.seek(0, 'e'); + _rxBuf.writen(readByte, 'b'); + } + } while ((readByte & MASK_OP) != OP_ARB) + value[1] = readByte & 0x0F; + + //server.log(format("0x%04X", value.readn('w'))); value.seek(0, 'b'); + imp.wakeup(0, _parseRXBuffer.bindenv(this)); + + return value.readn('w'); + } + } + /* + * Performs a function call on the Arduino. This is intended as a way to + * trigger additional functionality on the Arduino. There are 30 + * user-modifiable custom functions available in the Arduino code, with id + * numbers 1-30. + */ + function functionCall(id, arg = "", cb = null) { + assert (typeof id == "integer"); + if (typeof arg != "string") throw "Function call argument must be type string"; + if (id < 1 || id > 30) throw "Invalid function id: " + id; + + // Clear Arduino function buffer + _serial.write(OP_CALL); + _serial.flush(); + // Send function argument + _serial.write(arg); + // Initiate function call + _serial.write(OP_CALL | id); + // Register callback + if (cb != null) { + _functioncb[id] <- cb; + } + _serial.flush(); + } + + // -------------------- PRIVATE METHODS -------------------- // + /* + * Processes the buffer of pending received data. ASCII data is transcribed + * to the function return value buffer, while opcodes are executed. + */ + function _parseRXBuffer() { + local buf = _rxBuf; + _rxBuf = blob(); + buf.seek(0, 'b'); + local readByte = 0; + + while (!buf.eos()) { + readByte = buf.readn('b'); + if (readByte & 0x80) { + // Interpret as Opcode + //server.log(format("Opcode: 0x%02X", readByte)); + switch (readByte & MASK_OP) { + case OP_DIGITAL_WRITE_0: + local addr = readByte & MASK_DIGITAL_ADDR; + // Call callback if one has been assigned + if (addr in _digitalReadcb) { + imp.wakeup(0, function() { + (delete _digitalReadcb[addr])(0); + }.bindenv(this)); + } + break; + case OP_DIGITAL_WRITE_1: + local addr = readByte & MASK_DIGITAL_ADDR; + // Call callback if one has been assigned + if (addr in _digitalReadcb) { + imp.wakeup(0, function() { + (delete _digitalReadcb[addr])(1); + }.bindenv(this)); + } + break; + case OP_ANALOG: + local addr = readByte & MASK_ANALOG_ADDR; + local value = blob(2); + value[0] = (buf.readn('b') & 0x0F) | ((buf.readn('b') & 0x0F) << 4); + value[1] = buf.readn('b') & 0x0F; + // Call callback if one has been assigned + if (addr in _analogReadcb) { + imp.wakeup(0, function() { + (delete _analogReadcb[addr])(value.readn('w')); + }.bindenv(this)); + } + break; + case OP_CALL: + local addr = readByte & MASK_CALL; + local buf = _funcBuf; + _funcBuf = blob(); + buf.seek(0, 'b'); + // Call callback if one has been assigned + if (addr in _functioncb) { + imp.wakeup(0, function() { + (delete _functioncb[addr])(buf); + }.bindenv(this)); + } + break; + } + + } else { + // Save ASCII data to function return buffer + _funcBuf.seek(0, 'e'); + if (readByte == 0) + readByte = ' '; + _funcBuf.writen(readByte, 'b'); + } + } + } + /* + * UART data available event handler. Copies any available data to rxBuf, + * then calls _parseRXBuffer to interpret it. + */ + function _uartEvent() { + _rxBuf.seek(0, 'e'); + _rxBuf.writeblob(_serial.readblob()); + imp.wakeup(0, _parseRXBuffer.bindenv(this)); + } + + /* + * Compares the version string sent from the Arduino to a target version. + * By default, the Arduino sketch transmits its version on startup and then + * calls function 0 (0xE0). Assigning _versionCheck as the callback for + * functioncb[0] will perform a check on that version string. + */ + function _versionCheck(data) { + local versionString = format("%s", data.tostring()); + server.log(versionString); + if (!versionString.find(version[0] + "." + version[1] + "." + version[2])) { + server.log("Library version " + version[0] + "." + version[1] + "." + version[2]) + server.error("Impeeduino version mismatch!"); + } + } +} \ No newline at end of file diff --git a/hardware/impeeduino/programmer/README.md b/hardware/impeeduino/programmer/README.md new file mode 100644 index 0000000..70b166a --- /dev/null +++ b/hardware/impeeduino/programmer/README.md @@ -0,0 +1,42 @@ +# Impeedunio Programmer + +This firmware will allow you to program the ATmega328 built into the Impeeduino. +It parses Intel HEX files delivered via HTTP POST (form) and implements the STK500v1 serial protocol to talk to the connected ATmega328. + + +## Usage + +This is not a library class. It represents an entire application for programming the Arduino via an Imp with a HEX file. +### Hardware Setup +1. Insert an imp001 into the SD card slot on the left side of the board. +2. Then power the board via either the 7-12V DC jack or the USB connector. The green LED should light up to indicate power is connected. +3. [Blink up](https://electricimp.com/docs/gettingstarted/blinkup/) the imp001 to your account and assign it to a new model. +4. Copy the squirrel code in [impeeduino_programmer.agent.nut](./impeeduino_programmer.agent.nut) to the agent section and the code in [impeeduino_programmer.device.nut](./impeeduino_programmer.device.nut) to the device section. +5. Hit "Build and Run," and verify that the agent and device restart in the log console. +6. Visit the agent URL in a browser and follow the instructions to upload a `.HEX` file generated from the Arduino IDE. + +## Burning a Bootloader +The ATmega328 processors built into the Impeeduinos does not come with the bootloader preinstalled. In order to upload code over the serial port, it is necessary to install a compatible bootloader. This is a one-time operation that only has to be performed on new Impeeduino boards. + +You will need to install the "[optiboot](https://code.google.com/p/optiboot/)" bootloader using an ICSP cable. At the time of writing, the latest version was [v5.0a](https://code.google.com/p/optiboot/downloads/detail?name=optiboot-v5.0a.zip). +To do this you will need an ISP or use [another Ardiuno as the ISP](http://arduino.cc/en/Tutorial/ArduinoISP) and the ArduinoISP sketch. + +You might need to adjust the signature of the ATmega328P in the avrdude configuration to make avrdude think its an ATmega328P. This is on line 8486 on my installation. +/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf + + From: signature = 0x1e 0x95 0x0f; + To: signature = 0x1e 0x95 0x14; + +Once the ATMega is programmed you can continue to talk to it over the serial port. + +Note, if you are using the [SparkFun Imp Shield](https://www.sparkfun.com/products/11401) then you may need to reverse the +logic of the RESET pin. Where you see RESET.write(1), change it to RESET.write(0) and vice versa. + +## Contributors + +- Aron +- Sunny (Update documentation) + +## License + +The Impeeduino programmer is provided under the [MIT License](../LICENSE). diff --git a/hardware/impeeduino/impeeduino.agent.nut b/hardware/impeeduino/programmer/impeeduino_programmer.agent.nut similarity index 100% rename from hardware/impeeduino/impeeduino.agent.nut rename to hardware/impeeduino/programmer/impeeduino_programmer.agent.nut diff --git a/hardware/impeeduino/impeeduino.device.nut b/hardware/impeeduino/programmer/impeeduino_programmer.device.nut similarity index 100% rename from hardware/impeeduino/impeeduino.device.nut rename to hardware/impeeduino/programmer/impeeduino_programmer.device.nut