The first version of the working code.
Let me mention here that this is my first project with Arduino and I never used c++ before. But I was intent and did not give up.
I will clean the code but at least it works well. There are still some unused function what I found in other's similar projects. I left them in the code because I will use them later.
Big thanks for @Manu. His code was very useful for my project: https://hackaday.io/project/27439-smart-car-radio
/*
############################################################################################################
# This code is meant to interface a 6-wire Renault Twingo / Clio steering wheel remote control with #
# a JVC car radio equipped with a 'steering wheel remote input'. Hardware used is an Arduino Nano-clone. #
############################################################################################################
The steering wheel remote connection on the radio (blue/yellow wire or Tip in case of a TS connector)
is connected to a pull-up resistor in the radio circuitry.
Data is sent in the form of pulse interval modulation, meaning the interval following a pulse determines if we're sending a 0 or a 1.
Pulses are sent by pulling the radio's input to ground.
I'm driving an optocoupler to pull the radio's input to ground, so a HIGH Arduino output makes for a LOW radio input (= a pulse).
Whenever I refer to HIGH or LOW, I'm talking about the Arduino output.
############
# JVC part #
############
_____________
+---------|_____________|---------+
| .1 ·2 ·3 ·4 ·5 ·6 ·7 ·8 |
| |
| ˙9 ·10 ·11 ·12 ·13 ·14 ·15 ·16 |
+---------------------------------+
1 Ground (black)
2 +12v ignition (red)
3 Power Antenna (blue/white) *** Arduino INPUT (Is powered on?)
4 Not connected (not connected)
5 Right rear + (grey)
6 Right rear - (grey/black)
7 Left rear - (white/black)
8 Left rear + (white)
9 +12v battery (yellow)
10 +12v illumination (orange/white)
11 Not connected (not connected)
12 Remote control (blue/yellow) *** Arduino OUTPUT
13 Right front + (purple)
14 Right front - (purple/black)
15 Left front - (green/black)
16 Left front + (green)
Protocol specifications for pin12:
Pulse width 527.5 µs
Pulse interval for sending 0 1055.0 µs (HIGH for 1 pulse width, LOW for 1 pulse width)
Pulse interval for sending 1 2110.0 µs (HIGH for 1 pulse width, LOW for 3 pulse widths)
Note: since the delayMicroseconds() function accepts only unsigned integers, we're using a pulse width of 527 µs
Data packets are constructed as follows:
HEADER always LOW (1 pulse width), HIGH (16 pulse widths), LOW (8 pulse widths)
START BIT always 1
ADDRESS 7-bits (0x00 to 0x7F), send LSB first, always 0x47 for JVC KD-R531, probably the same for all JVC car radio's
COMMAND 7-bits (0x00 to 0x7F), send LSB first, see next section for list of known commands for JVC KD-R531
STOP BITS always 1, 1
Note: the ADDRESS, COMMAND and STOP BITS are repeated 3 times to ensure the radio properly receives them.
Known commands for JVC KD-R531:
HEX DEC BIN(7b) FUNCTION
0x04 4 0000100 Volume +
0x05 5 0000101 Volume -
0x08 8 0001000 Source cycle
0x0D 13 0001101 Equalizer preset cycle
0x0E 14 0001110 Mute toggle / Play/pause toggle
0x12 18 0010010 Tuner Search + / Track + (and Manual Tune + / Fast Forward with press & hold)
0x13 19 0010011 Tuner Search - / Track - (and Manual Tune - / Fast Rewind with press & hold)
0x14 20 0010100 Tuner Preset + / USB Folder +
0x15 21 0010101 Tuner Preset - / USB Folder -
0x37 55 0110111 UNKNOWN, appears to be a sort of reset as well as a display test
0x58 88 1011000 UNKNOWN, displays 'SN WRITING' where WRITING is blinking
Special thanks to: https://www.avforums.com/threads/jvc-stalk-adapter-diy.248455/page-3
#############
# Clio part #
#############
ISO 10487 defines a standard for connectors for the head unit to the car's electrical system, consisting of a system of four different connectors typically used in head units for car audio.
Power (A)
+-----------------+
| |1 |3 |5 |7 |
_| |
|_ |2 |4 |6 |8 |
+-----------------+
A1 Speed signal (pink) *** Arduino INPUT
A2 Phone mute (not connected) --- ---
A3 Not connected (not connected) --- ---
A4 +12v battery (red/pink) --- *** JVC
A5 Antenna motor (grey) Arduino *** *** JVC
A6 Illumination (blue) --- *** JVC
A7 +12v switched (yellow/pink) *** Arduino *** JVC
A8 Ground (black) *** Arduino *** JVC
Miscellaneous (C)
+--------------+
++ - - |
++ 5 2 |
| - - +-+
++ 6 3 |
++ - - +-+
| 4 1 +-+
+----------------+
1 CANL (brown)
2 CANH (purple)
3 CANH (purple)
4 CANL (brown)
5 Radio ON (grey) *** Arduino OUTPUT
6 Not connected
Special thanks to: http://megane.com.pl/topic/47797-wyswietlacz-radia-update-list-protokol/
Protocol: CAN 11bit, 1Mbps
################
# Arduino part #
################
+---+
+------|USB|------+
|[ ]J1 +---+ |
|( )TX0 RAW( )| RAW
|( )RXI GND( )| GND
GND |( )GND RST( )| RESET
GND |( )GND VCC( )| VCC
|( )2 A3( )|
INT0 |(I)3 /\ A2( )|
|(O)4 / \ A1( )|
|(O)5 \ / A0( )|
|(I)6 \/ 15( )| SCK
|( )7 14( )| MISO
|( )8 16( )| MOSI
|(I)9 10( )|
+-----------------+
ProMicro
I = Input
O = Output
3 CAN_INTERRUPT_PIN
4 JVC_REMOTE_PIN
5 CLIO_DISPLAY_PIN
6 JVC_ANTENNA_PIN
9 CLIO_SPEEDSIGNAL_PIN
*/
#include <SPI.h>
#include "mcp_can.h" // https://github.com/coryjfowler/MCP_CAN_lib
#include "mcp_can_dfs.h"
#include "SimpleTimer.h"
SimpleTimer timer;
// Define JVC commands
const int JVC_VOL_UP = 0x04; // Volume Up
const int JVC_VOL_DOWN = 0x05; // Volume Down
const int JVC_SOURCE = 0x08;
//const int JVC_EQUALIZER = 0x0D;
const int JVC_MUTE = 0x0E;
const int JVC_TRACK_FORW = 0x12;
const int JVC_TRACK_BACK = 0x13;
const int JVC_FOLDER_FORW = 0x14;
const int JVC_FOLDER_BACK = 0x15;
const int JVC_VOICE_CONTROL = 0x1A;
//const int JVC_UNKNOWN1 = 0x37;
//const int JVC_UNKNOWN2 = 0x58;
const int JVC_PULSE_WIDTH = 527; // Pulse width in µs
const int JVC_ADDRESS = 0x47; // Address that the radio responds to
const int JVC_REMOTE_PIN = 4; // Connect optocoupler input through a 1k resistor to this pin
const int CAN_INTERRUPT_PIN = 3;
const int CAN_SPI_CS_PIN = 10;
MCP_CAN CAN(CAN_SPI_CS_PIN); // Set CS pin
unsigned char CAN_MESSAGE_LENGTH = 0;
unsigned char CAN_RECEIVED_MESSAGE[8];
long unsigned int CAN_ID;
byte CAN_SEND_RESULT;
unsigned char CAN_TEST_MESSAGE[] = {
0x10, /* Set text */
0x1C, /* If we want to inform about them display here 0x1C and additional bytes further */
0x7F, /* ??? */
0x55, /* For old type: station number not set */
0x55, /* Normal text */
'1', '2', '3', '4', '5', '6', '7', '8', /* 8 characters for the old type */
0x10, /* Serparator? */
'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0', /* 12 characters for new type + null byte */
};
const unsigned char CLIO_CAN_KEEPALIVE[8] = {0x79, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
const unsigned char CLIO_CAN_KEEPALIVE_ACK[8] = {0x69, 0x00, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_5C1_MESSAGE[8] = {0x74, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
const unsigned char CLIO_CAN_REMOTE_VOL_UP[8] = {0x03, 0x89, 0x00, 0x03, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_VOL_UP_LONG[8] = {0x03, 0x89, 0x00, 0x43, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_VOL_DOWN[8] = {0x03, 0x89, 0x00, 0x04, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_VOL_DOWN_LONG[8] = {0x03, 0x89, 0x00, 0x44, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_PAUSE[8] = {0x03, 0x89, 0x00, 0x05, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_PAUSE_LONG[8] = {0x03, 0x89, 0x00, 0x85, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SOURCE_RIGHT[8] = {0x03, 0x89, 0x00, 0x01, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SOURCE_RIGHT_LONG[8] = {0x03, 0x89, 0x00, 0x81, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SOURCE_LEFT[8] = {0x03, 0x89, 0x00, 0x02, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SOURCE_LEFT_LONG[8] = {0x03, 0x89, 0x00, 0x82, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SELECT[8] = {0x03, 0x89, 0x00, 0x00, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_SELECT_LONG[8] = {0x03, 0x89, 0x00, 0x80, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_ROLL_DOWN[8] = {0x03, 0x89, 0x01, 0x01, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_ROLL_UP[8] = {0x03, 0x89, 0x01, 0x41, 0xA2, 0xA2, 0xA2, 0xA2};
const unsigned char CLIO_CAN_REMOTE_ACK[8] = {0x74, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
/* Remote - JVC remapping
vol_up - Vol+
vol_up_long - Vol++
vol_down - Vol-
vol_down_long - Vol--
mute - Mute
mute_long - Mute
source_left - FolderBack
source_left_long - TrackBack long
source_right - FolderForw
source_right_long - TrackForw long
select - Source
select_long - Voice Control
roll_up - TrackBack
roll_down - TrackForw
*/
String Txt = ""; // text to display
String savedTxt = ""; // new text waiting to be displayed
bool flagUpdateTxt = false;
const int CLIO_DISPLAY_PIN = 5; // Connect optocoupler input through a 1k resistor to this pin
const int JVC_ANTENNA_PIN = 6;
const int CLIO_SPEEDSIGNAL_PIN = 9;
unsigned long CLIO_SPEEDSIGNAL_duration;
byte CLIO_SPEED;
void setup() {
Serial.begin(9600);
delay(1000);
Serial.println("Serial configured");
// CAN 11 bits 500kbauds
if (CAN.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK) // Baud rates defined in mcp_can_dfs.h
Serial.println("CAN Init OK.");
else
Serial.println("CAN Init Failed.");
CAN.setMode(MCP_NORMAL);
pinMode(CAN_INTERRUPT_PIN, INPUT); // start interrupt
pinMode(JVC_REMOTE_PIN, OUTPUT); // Set pin to output
digitalWrite(JVC_REMOTE_PIN, LOW); // Output LOW to make sure optocoupler is off
pinMode(CLIO_DISPLAY_PIN, OUTPUT);
digitalWrite(CLIO_DISPLAY_PIN, LOW); // Output LOW to make sure optocoupler is off
pinMode(JVC_ANTENNA_PIN, INPUT_PULLUP);
pinMode(CLIO_SPEEDSIGNAL_PIN, INPUT_PULLUP);
CLIO_CAN_startSync();
delay(1);
CLIO_CAN_syncOK();
delay(1);
// startSync();
// delay(1);
CLIO_CAN_syncDisp(); // triggers 1c1 and 0a9 on the display side: response 5C1 and 4A9
delay(10);
CAN.sendMsgBuf(0x5C1, 0, 8, CLIO_CAN_5C1_MESSAGE);
CAN.sendMsgBuf(0x4A9, 0, 8, CLIO_CAN_REMOTE_ACK);
CLIO_CAN_initDisplay();
delay(1);
CLIO_CAN_registerDisplay();
delay(1);
CLIO_CAN_enableDisplay();
delay(50);
delay(10);
timer.setInterval(700, CLIO_CAN_syncOK);
Serial.println("Setup done");
}
void loop() {
// receive CAN
if (!digitalRead(CAN_INTERRUPT_PIN))
{
CAN.readMsgBuf(&CAN_ID, &CAN_MESSAGE_LENGTH, CAN_RECEIVED_MESSAGE); // read data, CAN_MESSAGE_LENGTH: data length, buf: data buf
if (CAN_ID == 0x3CF && memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_KEEPALIVE_ACK, 8) == 0) { // pong received
// CAN.sendMsgBuf(0x3DF, 0, 8, CLIO_CAN_KEEPALIVE);
//Serial.println("Pion");Serial.println();
}
// else if(CAN_ID == 0x521) { // TODO: check entire frame (not just can ID)
// Serial.println("TEXT ACK received"); Serial.println();
// }
else if (CAN_ID == 0x1C1) {
CAN.sendMsgBuf(0x5C1, 0, 8, CLIO_CAN_5C1_MESSAGE);
}
// Receiving from the steering wheel remote
else if (CAN_ID == 0x0A9) {
CAN_SEND_RESULT = CAN.sendMsgBuf(0x4A9, 0, 8, CLIO_CAN_REMOTE_ACK);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("CLIO_CAN_REMOTE_ACK Message Sent Successfully!");
else
Serial.println("Error Sending CLIO_CAN_REMOTE_ACK Message...");
if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_PAUSE, 8) == 0) {
Serial.println("mute received");
JVC_SendCommand(JVC_MUTE);
Serial.println("Mute was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_PAUSE_LONG, 8) == 0) {
Serial.println("long mute received");
JVC_SendCommand(JVC_MUTE);
Serial.println("Mute was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_VOL_UP, 8) == 0) {
Serial.println("Vol+ received");
JVC_SendCommand(JVC_VOL_UP);
Serial.println("Vol+ was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_VOL_UP_LONG, 8) == 0) {
Serial.println("Vol++ received");
JVC_SendCommand(JVC_VOL_UP); //need to be fix
delay(50);
JVC_SendCommand(JVC_VOL_UP);
Serial.println("Vol++ was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_VOL_DOWN, 8) == 0) {
Serial.println("Vol- received");
JVC_SendCommand(JVC_VOL_DOWN);
Serial.println("Vol- was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_VOL_DOWN_LONG, 8) == 0) {
Serial.println("Vol-- received");
JVC_SendCommand(JVC_VOL_DOWN); //need to be fix
delay(50);
JVC_SendCommand(JVC_VOL_DOWN);
Serial.println("Vol-- was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SELECT, 8) == 0) {
Serial.println("Select received");
JVC_SendCommand(JVC_SOURCE); //need to be fix
Serial.println("Source was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SELECT_LONG, 8) == 0) {
Serial.println("Long select received");
JVC_SendCommand(JVC_VOICE_CONTROL); //need to be fix
Serial.println("Voice Control was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SOURCE_LEFT, 8) == 0) {
Serial.println("Source left received");
JVC_SendCommand(JVC_FOLDER_BACK); //need to be fix
Serial.println("Folder back was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SOURCE_LEFT_LONG, 8) == 0) {
Serial.println("Long Source left received");
JVC_SendCommand(JVC_FOLDER_BACK); //need to be fix
Serial.println("Folder back was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SOURCE_RIGHT, 8) == 0) {
Serial.println("Source right received");
JVC_SendCommand(JVC_FOLDER_FORW); //need to be fix
Serial.println("Folder forw was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_SOURCE_RIGHT_LONG, 8) == 0) {
Serial.println("Long Source right received");
JVC_SendCommand(JVC_FOLDER_FORW); //need to be fix
Serial.println("Folder forw was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_ROLL_DOWN, 8) == 0) {
Serial.println("Roll down received");
JVC_SendCommand(JVC_TRACK_FORW); //need to be fix
Serial.println("Track forw was sent to JVC");
}
else if (memcmp(CAN_RECEIVED_MESSAGE, CLIO_CAN_REMOTE_ROLL_UP, 8) == 0) {
Serial.println("Roll up received");
JVC_SendCommand(JVC_TRACK_BACK); //need to be fix
Serial.println("Track back was sent to JVC");
}
else {
Serial.print("CAN ID: ");
Serial.print(CAN_ID, HEX);
Serial.print(" Data: ");
for(int i = 0; i<CAN_MESSAGE_LENGTH; i++) // Print each byte of the data
{
if(CAN_RECEIVED_MESSAGE[i] < 0x10) // If data byte is less than 0x10, add a leading zero
{
Serial.print("0");
}
Serial.print(CAN_RECEIVED_MESSAGE[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
else {
Serial.print("CAN ID: ");
Serial.print(CAN_ID, HEX);
Serial.print(" Data: ");
for(int i = 0; i<CAN_MESSAGE_LENGTH; i++) // Print each byte of the data
{
if(CAN_RECEIVED_MESSAGE[i] < 0x10) // If data byte is less than 0x10, add a leading zero
{
Serial.print("0");
}
Serial.print(CAN_RECEIVED_MESSAGE[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
// TODO: periodically send sync command to display (100ms -> 1sec)
//CLIO_CAN_syncOK();
timer.run();
// if (flagUpdateTxt) {
// Serial.println("flagUpdate");
// Txt = savedTxt;
// flagUpdateTxt = false;
// Serial.print(Txt);
// Serial.println(Txt.length() - 1); // doesn't count ending char
// pre-filter known bad strings:
// Txt.replace("OI FM", "OUI FM");
// Txt.replace("%antexts1", "");
// Txt.replace("%antexts2", "");
// Serial.println(Txt);
// }
// static int count = 0;
// if(Txt == "") { // empty string at startup
// display8ByteString("DOROTTYA");
// if(count++ > 25) {
// Txt == "";
// display8ByteString(" ");
// }
// }
// String teststr = "ZACK DE LA ROCHA - WE WANT IT ALL";
//scrollDisplay(teststr/*Txt*/);
//wordScroll(/*teststr*/Txt);
//semiScroll(/*teststr*/Txt);
//delay(200);
}
void printDisp(char text_to_display) {
char *arg;
int aNumber;
String s = "";
// Serial.println("Display: ");
arg = text_to_display;
while (arg != NULL) {
s += arg;
arg = text_to_display;
if (arg != NULL) { // check if space separator (if several spaces, just one is inserted)
s += ' ';
}
if (s.length() > 8) { // check if length max
s.remove(8);
}
}
// Serial.print(s); Serial.print(" ("); Serial.print(s.length()); Serial.println(")");
// Serial.println();
while (s.length() < 8) {
s += ' '; // pad string with spaces to make 8 byte string
}
String TextCmd = "";
TextCmd += '\x10';
TextCmd += '\x19';
TextCmd += '\x76';
TextCmd += '\x60';
TextCmd += '\x01';
TextCmd += s;
TextCmd += '\x10';
TextCmd += s;
TextCmd += " "; // 4 spaces to make a 12-byte string
char charArray[28] = {'\0'};
TextCmd.toCharArray(charArray, 27);
// Serial.println(TextCmd);
// for(int i = 0; i < 28; i++) {
// Serial.print(charArray[i], HEX); Serial.print(' ');
// }
// Serial.println();
send_to_display(0x121, (byte *)(charArray), 27);
}
void send_to_display(word id, byte * data, byte datasz) {
do_send_to(id, data, datasz, 0x81);
}
void do_send_to(word id, byte * data, byte datasz, byte filler) {
unsigned char packet[8] = {'\0'};
byte packetnum = 0, i, slen = datasz;
while (slen > 0) {
i = 0;
if (packetnum > 0) {
packet[0] = 0x20 + packetnum; /* Another package with one message */
i++;
}
while ((i < 8) && (slen > 0)) {
packet[i] = *data;
data++;
slen--;
i++;
}
for (; i < 8; i++)
packet[i] = filler;
CAN.sendMsgBuf((unsigned long)(id), 0, 8, packet);
//canTransmit((unsigned long)(id), packet, 8);
// Serial.println(packetnum);
packetnum++;
delay(2); /* TODO: Here we should wait for the display / radio response instead of the delay */
if (!digitalRead(CAN_INTERRUPT_PIN))
{
CAN.readMsgBuf(&CAN_ID, &CAN_MESSAGE_LENGTH, CAN_RECEIVED_MESSAGE); // read data, CAN_MESSAGE_LENGTH: data length, buf: data buf
// Serial.print(CAN.getCanId(), HEX); Serial.println(" received");
}
}
}
void display8ByteString(String s) {
if (s.length() != 8 ) {
Serial.println("String must be 8 bytes long");
return;
}
String TextCmd = "";
TextCmd += '\x10';
TextCmd += '\x19';
TextCmd += '\x76';
TextCmd += '\x60';
TextCmd += '\x01';
TextCmd += s;
TextCmd += '\x10';
TextCmd += s.substring(0, 7);
TextCmd += " "; // 4 spaces to make a 12-byte string
char charArray[28] = {'\0'};
TextCmd.toCharArray(charArray, 27);
// Serial.println(TextCmd);
// for(int i = 0; i < 28; i++) {
// Serial.print(charArray[i], HEX); Serial.print(' ');
// }
// Serial.println();
send_to_display(0x121, (byte *)(charArray), 27);
}
void scrollDisplay(String s) {
if(s.length() == 0) {
return;
}
s.trim(); // remove \r\n at the end of string
//Serial.print(s); Serial.print(" ("); Serial.print(s.length()); Serial.println(")");
//Serial.println();
String s8 = s.substring(0,8);
while( s8.length() < 8) {
s8 += ' '; // pad to 8 byte string
}
//Serial.println(s8);
// display 1st 8 bytes
display8ByteString(s8);
CLIO_CAN_syncOK(); // every 100ms to 1sec
delay(1000);
CLIO_CAN_syncOK(); // every 100ms to 1sec
// scroll after 8 bytes
for(int i=8;i<s.length();i++) {
//Serial.println(i);
if( (i+8) >= s.length()) {
// pad last section to 8 bytes
s8 = s.substring(i,s.length());
while(s8.length()<8) {
s8 += ' '; // pad string with spaces to make 8 byte string
}
i=s.length(); // stop scrolling at last section
}
else {
s8 = s.substring(i,i+8);
}
//Serial.println(s8);
display8ByteString(s8);
delay(500);
CLIO_CAN_syncOK(); // every 100ms to 1sec
}
}
void semiScroll(String s)
{
if (s.length() == 0) {
return;
}
s.trim(); // remove \r\n at the end of string
String saveds = s;
int Idx = 0; // find the " - " index
Idx = s.indexOf(" - ");
if (Idx == -1) {
exit;
}
//Serial.println(Idx);
s = saveds.substring(0, Idx);
//Serial.println(s);
// Scroll artist string (until " - " token)
String s8 = s.substring(0, 8);
while ( s8.length() < 8) {
s8 += ' '; // pad to 8 byte string
}
//Serial.println(s8);
// display 1st 8 bytes
display8ByteString(s8);
CLIO_CAN_syncOK(); // every 100ms to 1sec
delay(1000);
CLIO_CAN_syncOK(); // every 100ms to 1sec
// scroll after 8 bytes
if (s.length() > 8) {
for (int i = 1; i < s.length(); i++) {
//Serial.println(i);
if ( (i + 8) >= s.length()) {
// pad last section to 8 bytes
s8 = s.substring(i, s.length());
while (s8.length() < 8) {
s8 += ' '; // pad string with spaces to make 8 byte string
}
i = s.length(); // stop scrolling at last section
}
else {
s8 = s.substring(i, i + 8);
}
//Serial.println(s8);
display8ByteString(s8);
delay(500);
CLIO_CAN_syncOK(); // every 100ms to 1sec
}
}
// print separator
display8ByteString("--------");
CLIO_CAN_syncOK(); // every 100ms to 1sec
delay(500);
// Scroll song name
s = saveds.substring(Idx + 3, saveds.length());
//Serial.println(s);
s8 = s.substring(0, 8);
while ( s8.length() < 8) {
s8 += ' '; // pad to 8 byte string
}
//Serial.println(s8);
// display 1st 8 bytes
display8ByteString(s8);
CLIO_CAN_syncOK(); // every 100ms to 1sec
delay(1000);
CLIO_CAN_syncOK(); // every 100ms to 1sec
if (s.length() <= 8) {
CLIO_CAN_syncOK();
return;
}
// scroll after 8 bytes
for (int i = 1; i < s.length(); i++) {
//Serial.println(i);
if ( (i + 8) >= s.length()) {
// pad last section to 8 bytes
s8 = s.substring(i, s.length());
while (s8.length() < 8) {
s8 += ' '; // pad string with spaces to make 8 byte string
}
i = s.length(); // stop scrolling at last section
}
else {
s8 = s.substring(i, i + 8);
}
//Serial.println(s8);
display8ByteString(s8);
delay(500);
CLIO_CAN_syncOK(); // every 100ms to 1sec
}
// print end separator
display8ByteString("////////");
CLIO_CAN_syncOK(); // every 100ms to 1sec
delay(500);
}
void CLIO_CAN_startSync() {
unsigned char startSyncMsg[8] = {0x7A, 0x01, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
CAN_SEND_RESULT = CAN.sendMsgBuf(0x3DF, 0, 8, startSyncMsg);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("startSync Message Sent Successfully!");
else
Serial.println("Error Sending startSync Message...");
}
void CLIO_CAN_syncOK() {
CAN.sendMsgBuf(0x3DF, 0, 8, CLIO_CAN_KEEPALIVE);
/* if (CAN_SEND_RESULT == CAN_OK)
Serial.println("syncOK Message Sent Successfully!");
else
Serial.println("Error Sending syncOK Message...");*/
}
void CLIO_CAN_syncDisp() {
unsigned char syncDispMsg[8] = {0x70, 0x1A, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01};
CAN.sendMsgBuf(0x3DF, 0, 8, syncDispMsg);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("syncDisp Message Sent Successfully!");
else
Serial.println("Error Sending syncDisp Message...");
}
void CLIO_CAN_initDisplay() {
unsigned char initDispMsg[8] = {0x70, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
CAN_SEND_RESULT = CAN.sendMsgBuf(0x121, 0, 8, initDispMsg);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("initDisp Message Sent Successfully!");
else
Serial.println("Error Sending initDisp Message...");
}
void CLIO_CAN_registerDisplay() {
unsigned char registerDispMsg[8] = {0x70, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
CAN_SEND_RESULT = CAN.sendMsgBuf(0x1B1, 0, 8, registerDispMsg);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("registerDisp Message Sent Successfully!");
else
Serial.println("Error Sending registerDisp Message...");
}
void CLIO_CAN_enableDisplay() {
unsigned char enableDispMsg[8] = {0x04, 0x52, 0x02, 0xFF, 0xFF, 0x81, 0x81, 0x81};
CAN_SEND_RESULT = CAN.sendMsgBuf(0x1B1, 0, 8, enableDispMsg);
if (CAN_SEND_RESULT == CAN_OK)
Serial.println("enableDisp Message Sent Successfully!");
else
Serial.println("Error Sending enableDisp Message...");
}
void CLIO_CAN_clearDisplay() {
display8ByteString(" ");
}
/* JVC Functions */
// Send a command to the radio, including the header, start bit, address and stop bits
void JVC_SendCommand(unsigned char value) {
Serial.println("Sending JVC command");
unsigned char i;
JVC_Preamble(); // Send signals to precede a command to the radio
for (i = 0; i < 3; i++) { // Repeat address, command and stop bits three times so radio will pick them up properly
JVC_SendValue(JVC_ADDRESS); // Send the address
JVC_SendValue((unsigned char)value); // Send the command
JVC_Postamble(); // Send signals to follow a command to the radio
}
}
// Send a value (7 bits, LSB is sent first, value can be an address or command)
void JVC_SendValue(unsigned char value) {
unsigned char i, tmp = 1;
for (i = 0; i < sizeof(value) * 8 - 1; i++) {
if (value & tmp) // Do a bitwise AND on the value and tmp
JVC_SendOne();
else
JVC_SendZero();
tmp = tmp << 1; // Bitshift left by 1
}
}
// Signals to transmit a '0' bit
void JVC_SendZero() {
digitalWrite(JVC_REMOTE_PIN, HIGH); // Output HIGH for 1 pulse width
delayMicroseconds(JVC_PULSE_WIDTH);
digitalWrite(JVC_REMOTE_PIN, LOW); // Output LOW for 1 pulse width
delayMicroseconds(JVC_PULSE_WIDTH);
}
// Signals to transmit a '1' bit
void JVC_SendOne() {
digitalWrite(JVC_REMOTE_PIN, HIGH); // Output HIGH for 1 pulse width
delayMicroseconds(JVC_PULSE_WIDTH);
digitalWrite(JVC_REMOTE_PIN, LOW); // Output LOW for 3 pulse widths
delayMicroseconds(JVC_PULSE_WIDTH * 3);
}
// Signals to precede a command to the radio
void JVC_Preamble() {
// HEADER: always LOW (1 pulse width), HIGH (16 pulse widths), LOW (8 pulse widths)
digitalWrite(JVC_REMOTE_PIN, LOW); // Make sure output is LOW for 1 pulse width, so the header starts with a rising edge
delayMicroseconds(JVC_PULSE_WIDTH * 1);
digitalWrite(JVC_REMOTE_PIN, HIGH); // Start of header, output HIGH for 16 pulse widths
delayMicroseconds(JVC_PULSE_WIDTH * 16);
digitalWrite(JVC_REMOTE_PIN, LOW); // Second part of header, output LOW 8 pulse widths
delayMicroseconds(JVC_PULSE_WIDTH * 8);
// START BIT: always 1
JVC_SendOne();
}
// Signals to follow a command to the radio
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.