Compare commits

...

10 Commits

Author SHA1 Message Date
ef5e0f8792 Add Schematics - Formating Code 2021-03-24 14:39:37 +01:00
dbc9ea571a 1 2021-03-12 17:36:13 +01:00
515acb426f SoftSerialVersion 2021-03-04 17:27:47 +01:00
AK-Homberger
18928e0ba7 Update README.md 2020-12-16 13:45:15 +01:00
AK-Homberger
f9983f26e2 Update README.md 2020-12-16 13:43:47 +01:00
AK-Homberger
0c972092b7 Update README.md 2020-12-16 11:48:12 +01:00
AK-Homberger
11360deda3 Update README.md 2020-12-16 11:44:33 +01:00
AK-Homberger
8cc04492f7 Added commands via USB-Serial (for AvNav contol of AP) 2020-12-16 11:42:54 +01:00
AK-Homberger
df4d9e70cf Update README.md 2020-12-08 07:49:20 +01:00
AK-Homberger
2d2d5ae3b0 Update README.md 2020-11-28 10:27:10 +01:00
3 changed files with 495 additions and 5 deletions

View File

@@ -12,7 +12,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Version 1.4, 27.07.2020, AK-Homberger
// Version 1.5, 16.12.2020, AK-Homberger
#include <avr/pgmspace.h>
#include <RCSwitch.h>
@@ -35,6 +35,9 @@ Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RCSwitch mySwitch = RCSwitch();
String inputString = ""; // a String to hold incoming data
bool stringComplete = false; // whether the string is complete
const unsigned long Key_Minus_1 PROGMEM = 1111001; // Change values to individual values programmed to remote control
const unsigned long Key_Plus_1 PROGMEM = 1111002;
const unsigned long Key_Minus_10 PROGMEM = 1111003;
@@ -150,6 +153,9 @@ void setup() {
Serial1.begin( 4800, SERIAL_9N1 ); // Set the Seatalk modus - 9 bit
Serial1.setTimeout(5);
// reserve 20 bytes for the inputString:
inputString.reserve(20);
mySwitch.enableReceive(4); // RF Receiver on inerrupt 4 => that is pin 7 on Micro
pinMode(9, OUTPUT); // LED to show if keys are received
@@ -173,6 +179,7 @@ void BeepOn(void) {
sendDatagram(ST_BeepOn);
digitalWrite(20, HIGH);
//Serial.println("On");
beep_time = millis();
beep_status = true;
}
@@ -184,13 +191,33 @@ void BeepOff(void) {
if (beep_status == true && millis() > beep_time + BEEP_DURATION) {
sendDatagram(ST_BeepOff);
digitalWrite(20, LOW);
//Serial.println("Off");
beep_status = false;
}
}
void loop() {
/*
SerialEvent occurs whenever a new data comes in the hardware serial RX. This
routine is run between each time loop() runs, so using delay inside loop can
delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
if ((inChar != '\n') && (inChar != '\r')) inputString += inChar;
// if the incoming character is a newline or CR, set a flag so the main loop can react
if ((inChar == '\n') || (inChar == '\r')) {
stringComplete = true;
}
}
}
void loop() {
char AWS[4] = "";
unsigned long value = 0;
@@ -219,6 +246,23 @@ void loop() {
mySwitch.resetAvailable();
}
serialEvent(); // Read serial to detect command from USB (until Newline or CR)
if (stringComplete) {
// Compare string
if(inputString == "-1") value = Key_Minus_1;
if(inputString == "+1") value = Key_Plus_1;
if(inputString == "-10") value = Key_Minus_10;
if(inputString == "+10") value = Key_Plus_10;
if(inputString == "A") value = Key_Auto;
if(inputString == "S") value = Key_Standby;
// clear the string
inputString = "";
stringComplete = false;
}
if (value > 0 && millis() > key_time + KEY_DELAY) {
key_time = millis(); // Remember time of last key received

View File

@@ -0,0 +1,441 @@
/*
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Based on the AK-Homberger work
// By Laurent D.
// Version 1.0
#include <avr/pgmspace.h>
#include <SPI.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <RCSwitch.h> // May impact the display ....
#include <SoftwareSerial.h>
/* PINNOUT */
// Definition Value // ATMEGA HW PIN // GPIO // Role
// ================================== // =============== // ==== // =========
// Hardware SERIAL RX // ATMEGA328 #2 // // SeaTalk
// Hardware SERIAL TX // ATMEGA328 #3 // // SeaTalk
#define PIN_SOFTSERIAL_RX 3 // ATMEGA328 #5 // 3 // Debug Serial RW
#define PIN_SOFTSERIAL_TX 4 // ATMEGA328 #6 // 4 // Debug Serial TX
#define PIN_RF_433 0 // ATMEGA328 #4 // 2 // Interrupt 0 , RF433 Input Data
#define PIN_LED 6 // ATMEGA328 #12 // 6 // LED PIN
#define PIN_BUZZ 7 // ATMEGA328 #13 // 7 // Buzzer PIN
// SSD1306 SCL // ATMEGA328 #28 // // oled SSD1306 SCL Pin
// SSD1306 SDA // ATMEGA328 #27 // // oled SSD1306 SDA Pin
/* OTHER CONFIGS */
#define I2C_ADDRESS 0x3C // Addresses can be 0x3D OR 0x3C depending of manufacturer
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define SSD1306_RST_PIN -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define Auto_Standby_Support 1 // Set this to 1 to support Standby and Auto for Key 5 and 6
#define KEY_DELAY 300 // 300 ms break between keys
#define BEEP_DURATION 150 // 150 ms beep time
#define DEBUG
String inputString = ""; // a String to hold incoming data
bool stringComplete = false; // whether the string is complete
const unsigned long Key_Minus_1 PROGMEM = 13721111; // Change value_rfs to individual value_rfs programmed to remote control
const unsigned long Key_Plus_1 PROGMEM = 13722222;
const unsigned long Key_Minus_10 PROGMEM = 13723333;
const unsigned long Key_Plus_10 PROGMEM = 13724444;
const unsigned long Key_Auto PROGMEM = 13725555;
const unsigned long Key_Standby PROGMEM = 13726666;
const unsigned long Key_Lamp0 PROGMEM = 13727777;
const unsigned long Key_Lamp1 PROGMEM = 13728888;
const unsigned long Key_Lamp2 PROGMEM = 13729999;
const unsigned long Key_Lamp3 PROGMEM = 13720000;
// Seatalk datagrams
const PROGMEM uint16_t ST_NMEA_BridgeID[] = { 0x190, 0x00, 0xA3 };
const PROGMEM uint16_t ST_Minus_1[] = { 0x186, 0x21, 0x05, 0xFA };
const PROGMEM uint16_t ST_Minus_10[] = { 0x186, 0x21, 0x06, 0xF9 };
const PROGMEM uint16_t ST_Plus_1[] = { 0x186, 0x21, 0x07, 0xF8 };
const PROGMEM uint16_t ST_Plus_10[] = { 0x186, 0x21, 0x08, 0xF7 };
const PROGMEM uint16_t ST_Auto[] = { 0x186, 0x21, 0x01, 0xFE };
const PROGMEM uint16_t ST_Standby[] = { 0x186, 0x21, 0x02, 0xFD };
const PROGMEM uint16_t ST_BeepOn[] = { 0x1A8, 0x53, 0x80, 0x00, 0x00, 0xD3 };
const PROGMEM uint16_t ST_BeepOff[] = { 0x1A8, 0x43, 0x80, 0x00, 0x00, 0xC3 };
const PROGMEM uint16_t ST_Lamp0[] = { 0x130, 0x00, 0x00 };
const PROGMEM uint16_t ST_Lamp1[] = { 0x130, 0x00, 0x04 };
const PROGMEM uint16_t ST_Lamp2[] = { 0x130, 0x00, 0x08 };
const PROGMEM uint16_t ST_Lamp3[] = { 0x130, 0x00, 0x0C };
boolean blink = true;
unsigned long wind_timer = 0; // timer for AWS display
unsigned long beep_timer2 = 0; // timer to stop alarm sound
unsigned long bridge_timer = 0; // timer to send ST Bridge ID every 10 seconds
unsigned long key_time = 0; // time of last key detected
unsigned long beep_time = 0; // timer for beep duration
bool beep_status = false;
uint32_t value_rf = 0;
char AWS[4];
RCSwitch mySwitch = RCSwitch();
SSD1306AsciiWire oled;
SoftwareSerial SerialSoft(PIN_SOFTSERIAL_RX, PIN_SOFTSERIAL_TX); // RX, TX
boolean sendDatagram(const uint16_t data[]) {
int i = 0; int j = 0;
boolean ok = true;
int bytes;
unsigned int inbyte;
unsigned int outbyte;
bytes = (pgm_read_byte_near(data + 1) & 0x0f) + 3; // Messege length is minimum 3, additional bytes in nibble 4
while (j < 5 ) { // CDMA/CD 5 tries
while (Serial.available ()) { // Wait for silence on the bus
inbyte = (Serial.read());
delay(3);
}
ok = true;
for (i = 0; (i < bytes) & (ok == true); i++) { // Write and listen to detect collisions
outbyte = pgm_read_word_near(data + i);
Serial.write(outbyte);
delay(3);
if (Serial.available ()) {
inbyte = Serial.read(); // Not what we sent, collision!
if (inbyte != outbyte) {
ok = false;
// SerialSoft.println("ST - I<>O");
}
}
else {
ok = false; // Nothing received
// SerialSoft.println("ST - Nothing in");
}
}
if ( ok )return ok;
j++; // Collision detected
#ifdef DEBUG
SerialSoft.println("Collision");
// Display("Collision");
#endif
delay(random(2, 50)); // Random wait for next try
}
Display("Send Error");
return false;
}
void Display(char *s) {
size_t tsize;
char dsp[12];
oled.clear();
oled.setFont(Cooper19);
tsize = oled.strWidth(s);
oled.setCursor((oled.displayWidth()-tsize)/2, 0);
oled.println(s);
snprintf(dsp, sizeof dsp, "AWS: %s", AWS);
tsize = oled.strWidth(dsp);
oled.setCursor((oled.displayWidth()-tsize)/2, 20);
oled.println(dsp);
oled.setFont(X11fixed7x14);
// ultoa(value_rf, dsp, 8);
snprintf(dsp, sizeof dsp, "%" PRIu32, value_rf);
tsize = oled.strWidth(dsp);
oled.setCursor((oled.displayWidth()-tsize)/2, 40);
oled.println(dsp);
}
void printByte(unsigned int data) {
SerialSoft.print("byte : ");
SerialSoft.print("0x");
uint8_t MSB=byte(data>>8);
uint8_t LSB=byte(data);
if (MSB<0x10) {SerialSoft.print("0");} SerialSoft.print(MSB,HEX); SerialSoft.print(" ");
if (LSB<0x10) {SerialSoft.print("0");} SerialSoft.print(LSB,HEX); SerialSoft.print(" ");
SerialSoft.println("");
}
// Receive apparent wind speed from bus
int checkWind(char * AWS) {
unsigned int xx;
unsigned int y;
unsigned int inbyte;
int wind = -1;
if (Serial.available ()) {
inbyte = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
if (inbyte == 0x111) { // AWS Seatalk command - See reference from Thomas Knauf
delay(3);
inbyte = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
if (inbyte == 0x01) { // AWS Setalk command
delay(3);
xx = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
delay(3);
y = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
wind = (xx & 0x7f) + (y / 10); // Wind speed
if (wind < 100) itoa (wind , AWS, 10); // Greater 100 must be a receive error
}
}
}
return wind;
}
void setup() {
SerialSoft.begin( 9600 ); // Serial out put for function checks with PC
Serial.begin( 4800, SERIAL_9N1 ); // Set the Seatalk modus - 9 bit
Serial.setTimeout(5);
#ifdef DEBUG
SerialSoft.println(F("Setup Begin"));
#endif
// reserve 20 bytes for the inputString:
inputString.reserve(20);
pinMode(PIN_LED, OUTPUT); // LED to show if keys are received
digitalWrite(PIN_LED, HIGH);
pinMode(PIN_BUZZ, OUTPUT); // Buzzer to show if keys are received
digitalWrite(PIN_BUZZ, LOW);
Wire.begin();
Wire.setClock(400000L);
#if SSD1306_RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS, SSD1306_RST_PIN);
#else // SSD1306_RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // SSD1306_RST_PIN >= 0
oled.setFont(Cooper19);
oled.clear();
oled.println("Starting");
mySwitch.enableReceive(PIN_RF_433); // RF Receiver on inerrupt 4 => that is pin 7 on Micro OR pin2 on Nano
sendDatagram(ST_NMEA_BridgeID); // Send NMEA Seatakl BridgeID to make Seatalk to Seatalk NG converter happy
#ifdef DEBUG
SerialSoft.println(F("Setup Complete"));
#endif
Display("Started");
}
// Beep on if key received
void BeepOn(void) {
if (beep_status == true) return; // Already On
sendDatagram(ST_BeepOn);
digitalWrite(PIN_BUZZ, HIGH);
//SerialSoft.println("On");
beep_time = millis();
beep_status = true;
}
// Beep off after BEEP_TIME
void BeepOff(void) {
if (beep_status == true && millis() > beep_time + BEEP_DURATION) {
sendDatagram(ST_BeepOff);
digitalWrite(PIN_BUZZ, LOW);
//SerialSoft.println("Off");
beep_status = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the hardware serial RX. This
routine is run between each time loop() runs, so using delay inside loop can
delay response. Multiple bytes of data may be available.
*/
void serialEvent() { // NOT WORKING IN SOFT SERIAL
while (SerialSoft.available()) {
// get the new byte:
SerialSoft.println("received from serial");
char inChar = (char)SerialSoft.read();
// add it to the inputString:
if ((inChar != '\n') && (inChar != '\r')) inputString += inChar;
// if the incoming character is a newline or CR, set a flag so the main loop can react
if ((inChar == '\n') || (inChar == '\r')) {
stringComplete = true;
}
}
}
void loop() {
AWS[0] = '\0';
// uint32_t value_rf = 0;
value_rf = 0;
if (millis() > wind_timer + 2000 ) {
// Display("No Wdata"); // Show --- after about two seconds when no wind data is received
wind_timer = millis();
}
if (millis() > beep_timer2 + 3000 ) {
sendDatagram(ST_BeepOff); // Additional Beep off after three seconds to avoid constant alarm
beep_timer2 = millis();
}
if (millis() > bridge_timer + 10000 ) {
#ifdef DEBUG
SerialSoft.println("Send Bridge ID");
#endif
sendDatagram(ST_NMEA_BridgeID); // Send NMEA Seatakl BridgeID every 10 seconds to make Seatalk to Seatalk NG converter happy
bridge_timer = millis();
}
if (checkWind(AWS) > -1) {
Display(AWS);
wind_timer = millis();
}
if (mySwitch.available()) {
value_rf = mySwitch.getReceivedValue();
mySwitch.resetAvailable();
}
serialEvent(); // Read serial to detect command from USB (until Newline or CR)
if (stringComplete) {
// Compare string received from SerialSoft
if(inputString == "-1" ) value_rf = Key_Minus_1;
if(inputString == "+1" ) value_rf = Key_Plus_1;
if(inputString == "-10") value_rf = Key_Minus_10;
if(inputString == "+10") value_rf = Key_Plus_10;
if(inputString == "A" ) value_rf = Key_Auto;
if(inputString == "S" ) value_rf = Key_Standby;
// clear the string and reset status
inputString = "";
stringComplete = false;
}
if (value_rf > 0 && millis() > key_time + KEY_DELAY) {
#ifdef DEBUG
oled.clear();
oled.println("Value : ");
oled.println(value_rf);
SerialSoft.print(F("rf_code ")); SerialSoft.println(value_rf);
#endif
key_time = millis(); // Remember time of last key received
digitalWrite(PIN_LED, blink); // LED on/off
blink = !blink; // Toggle LED to show received key
switch(value_rf) {
case Key_Minus_1:
Display("min1");
sendDatagram(ST_Minus_1);
BeepOn();
break;
case Key_Plus_1:
Display("plus1");
sendDatagram(ST_Plus_1);
BeepOn();
break;
case Key_Minus_10:
Display("min10");
sendDatagram(ST_Minus_10);
BeepOn();
break;
case Key_Plus_10:
Display("plus10");
sendDatagram(ST_Plus_10);
BeepOn();
break;
case Key_Auto:
if(Auto_Standby_Support == 1) {
Display("Auto");
sendDatagram(ST_Auto);
BeepOn();
}
break;
case Key_Standby:
if(Auto_Standby_Support == 1) {
Display("Stby");
sendDatagram(ST_Standby);
BeepOn();
}
break;
case Key_Lamp0:
Display("Lamp 0");
sendDatagram(ST_Lamp0);
BeepOn();
break;
case Key_Lamp1:
Display("Lamp 1");
sendDatagram(ST_Lamp1);
BeepOn();
break;
case Key_Lamp2:
Display("Lamp 2");
sendDatagram(ST_Lamp2);
BeepOn();
break;
case Key_Lamp3:
Display("Lamp 3");
sendDatagram(ST_Lamp3);
BeepOn();
break;
case 101:
Display("Beep ON");
sendDatagram(ST_BeepOn);
BeepOn();
break;
case 102:
Display("Beep OFF");
sendDatagram(ST_BeepOff);
BeepOn();
break;
}
}
BeepOff();
}

View File

@@ -4,6 +4,10 @@ This repository shows how to build a Seatalk Autopilot Remote Control device bas
My special thanks to users of www.segeln-forum.de for testing.
With the 433 MHz sender you can send -1 / +1 degree and -10 / +10 degrees. With a 6 key KeyFob or an additional one you can also send Auto/Standby commands.
Since version 1.5 the AP remote control reacts also on commands received via USB-Serial interface. Supported commands: "-1", "+1", "-10", "+10", "A" and "S". Both Newline and or CR are accepted at end of command from serial. This interfcae shall allow external applications like AvNav to control the AP functions.
![Autopilot Remote](https://github.com/AK-Homberger/Seatalk-Autopilot-Remote-Control/blob/master/IMG_0857.JPG)
![Autopilot Schematic](https://github.com/AK-Homberger/Seatalk-Autopilot-Remote-Control/blob/master/Remote%20Pilot%20Schematics.png)
@@ -36,7 +40,7 @@ MFDs with Lighthose II will react on the Seatalk alarm function with a short bee
# Problem with some 74LS07 variants
It seems that there are different types of 74LS07 devices available with slightly different electrical charcteristics. See issue "R3 wrong value?" for details.
If you get a "Send Error" problem (with 12 Volt connected) then the solution is to lower the value of R3. It is important to check two voltages. With Satalk signal line high (12 Volt) the voltage on pin 3 of 74LS07 (or alternatively on cathode of D1, which is easier to measure) should be above 2 Volt. When Seatalk pulled to GND (low) the voltage on pin3 should be less than 0.8 Volt.
If you get a "Send Error" problem (with 12 Volt connected) then the solution is to lower the value of R3. It is important to check two voltages. With Satalk signal line high (12 Volt) the voltage on pin 3 of 74LS07 (or alternatively on cathode of D1, which is easier to measure) should be above 2 Volt. When Seatalk pulled to GND (low) the voltage on pin 3 should be less than 0.8 Volt.
# Software
@@ -83,7 +87,7 @@ Please be aware that this repository is only for educational purpose, to learn h
- J1 Connector, AKL 101-03, 3-pin, 5,08 mm [Link](https://www.reichelt.com/index.html?ACTION=446&LA=0&nbc=1&q=akl%20101-03)
- J2 RXB6 433 Mhz receiver [Link](https://www.makershop.de/en/module/funk/rxb6-433mhz-antenne/)
- J4 Arduino ProMicro, 5 Volt [Link](https://eckstein-shop.de/HIMALAYA-basic-Pro-micro-5V-16MHz-Arduino-mini-Leonardo-compatible-board)
- J4 Arduino ProMicro, 5 Volt [Link](https://eckstein-shop.de/HIMALAYA-basic-Pro-micro-5V-16MHz-Arduino-mini-Leonardo-compatible-board) oder [Link](https://www.amazon.de/Micro-ATmega32U4-Arduino-Leonardo-%C3%A4hnlich/dp/B01D0OI90U)
- U1 7805 (alternative: 173950578 MagI3C DC-DC Power Module) [Link](https://www.reichelt.com/de/en/voltage-regulator-up-to-1-5-a-positive-to-220--a-7805-p23443.html?&trstct=pos_0&nbc=1) or [Link2](https://www.digikey.de/product-detail/en/w-rth-elektronik/173950578/732-8243-5-ND/5725367?cur=EUR&lang=en)
- U2 7407N (alternative: 74LS07) [Link](https://www.reichelt.com/de/en/index.html?ACTION=446&LA=446&nbc=1&q=7407n) or [Link](https://csd-electronics.de/ICs/Logik/74LS/74LS07-DIP14::283.html?pk_campaign=google_shopping&pk_kwd=74LS07-DIP14&gclid=EAIaIQobChMIrLSWjb-O7AIVird3Ch0ClQEdEAQYBCABEgJCdPD_BwE)
- D1 Zenerdiode 5.1 Volt (0.5 Watt) [Link](https://www.reichelt.com/de/en/zener-diode-0-5-w-5-1-v-zf-5-1-p23137.html?&trstct=pos_0&nbc=1)
@@ -106,7 +110,8 @@ Remote Control (KeyFob): [Link](https://www.amazon.de/XCSOURCE-Elektrische-Univ
# Updates:
- Version 1.4 - 28.11.20: Changed R2 and R3 back to 68 KOhm / 27 kOhm.
- Version 1.5 - 16.12.20: Added command receive from USB-Serial to allow control from AvNav.
- Version 1.4 - 28.11.20: Changed R2 and R3 back to 68 KOhm / 27 kOhm. 6.8 KOhm would impact the Seatalk high voltage level too much.
- Version 1.4 - 27.11.20: Changed R2 and R3 to 6.8 KOhm to eliminate problems with some 74LS07 devices (see issue "R3 wrong value?" for details).
- Version 1.4 - 27.07.20: Use of millis() funtion to avoid delay() and timer counter in loop(). Improved detection of 433MHz keys.
- Version 1.3 - 13.12.19: Added definition for certain OLED displays that require this to work properly.