Compare commits
No commits in common. "54f73ccaf2f082e586818279ed737e1d1b5736e1" and "f34d81ad878f2c92e32512bcc50ad82353c73066" have entirely different histories.
54f73ccaf2
...
f34d81ad87
4 changed files with 0 additions and 447 deletions
|
@ -1,118 +0,0 @@
|
|||
#include <Wire.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static const int eeprom_addr = 0b1010000;
|
||||
|
||||
void write_addr(uint16_t addr) {
|
||||
Wire.write((uint8_t)(addr >> 8));
|
||||
Wire.write((uint8_t)addr);
|
||||
}
|
||||
|
||||
void write_page(uint8_t page[128], uint16_t page_addr) {
|
||||
uint8_t bytes_written = 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int ret;
|
||||
int write_bytes = min(128 - bytes_written, 30);
|
||||
do {
|
||||
Wire.beginTransmission(eeprom_addr);
|
||||
write_addr(page_addr + bytes_written);
|
||||
Wire.write(page + bytes_written, write_bytes);
|
||||
ret = Wire.endTransmission();
|
||||
delay(5);
|
||||
} while (ret != 0);
|
||||
bytes_written += write_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_sumcomplement(uint8_t data[128], uint16_t checksum) {
|
||||
uint16_t sum = 0;
|
||||
for (int i = 0; i<128; i += 2) {
|
||||
sum += (data[i+1] << 8) | data[i];
|
||||
}
|
||||
sum += checksum;
|
||||
return !sum;
|
||||
}
|
||||
|
||||
volatile uint8_t pageData[128] = {};
|
||||
volatile uint16_t pageChecksum = 0;
|
||||
volatile uint16_t pageAddr = 0;
|
||||
volatile bool pageOk = false;
|
||||
volatile bool reicivingCart = false;
|
||||
volatile uint32_t last_page_update = 0;
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
Wire.begin();
|
||||
//Serial.println("Arduino started");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (reicivingCart && millis() - last_page_update > 1000) {
|
||||
Serial.print("TO");
|
||||
reicivingCart = false;
|
||||
pageOk = false;
|
||||
pageAddr = 0;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(500);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(500);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(500);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
}
|
||||
|
||||
if (pageOk) {
|
||||
pageOk = false;
|
||||
bool pageValid = check_sumcomplement((uint8_t *)pageData, pageChecksum);
|
||||
Serial.print(pageValid ? "ACK" : "NAK");
|
||||
if (pageValid) {
|
||||
write_page(pageData, pageAddr);
|
||||
pageAddr += 128;
|
||||
}
|
||||
|
||||
/* for (int i = 0; i<128; i++) {
|
||||
if (i%16 == 0)
|
||||
Serial.println();
|
||||
Serial.print(pageData[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println(pageChecksum, HEX);*/
|
||||
|
||||
Serial.print("OK");
|
||||
}
|
||||
}
|
||||
|
||||
void serialEvent() {
|
||||
static size_t read = 0;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
if (Serial.available() == 5) {
|
||||
char control[4] = {};
|
||||
Serial.readBytesUntil('\n', control, 4);
|
||||
Serial.read(); /* Drop the '\n' */
|
||||
if (!strncmp(control, "CART", 4)) {
|
||||
reicivingCart = true;
|
||||
Serial.print("OK");
|
||||
} else if (!strncmp(control, "DONE", 4)) {
|
||||
reicivingCart = false;
|
||||
} else {
|
||||
Serial.print("KO");
|
||||
}
|
||||
pageOk = false;
|
||||
pageAddr = 0;
|
||||
read = 0;
|
||||
}
|
||||
|
||||
while (reicivingCart && Serial.available() && read < 128) {
|
||||
read += Serial.readBytes((char *)pageData+read, min(128-read, 64));
|
||||
}
|
||||
if (reicivingCart && read >= 128) {
|
||||
pageOk = true;
|
||||
Serial.readBytes((char*)&pageChecksum, 2);
|
||||
read = 0;
|
||||
}
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
last_page_update = millis();
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
#include <Wire.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static const int eeprom_addr = 0b1010000;
|
||||
uint8_t pageData[128] = {};
|
||||
uint16_t pageAddr = 0;
|
||||
|
||||
void write_addr(uint16_t addr) {
|
||||
Wire.write((uint8_t)(addr >> 8));
|
||||
Wire.write((uint8_t)addr);
|
||||
}
|
||||
|
||||
uint16_t sum_complement(const uint8_t data[128]) {
|
||||
uint16_t sum = 0;
|
||||
for (int i = 0; i<128; i += 2) {
|
||||
sum += (data[i+1] << 8) | data[i];
|
||||
}
|
||||
return -sum;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char ret[4] = "";
|
||||
|
||||
// put your main code here, to run repeatedly:
|
||||
do {
|
||||
if (!Serial.readBytesUntil('\n', ret, 2))
|
||||
Serial.read();
|
||||
} while (strncmp(ret, "GO", 2) != 0);
|
||||
|
||||
// Reset the EEPROM internal address to 0. It autoincrements after.
|
||||
Wire.beginTransmission(eeprom_addr);
|
||||
write_addr(0);
|
||||
Wire.endTransmission(false);
|
||||
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
/*
|
||||
* We don't know the size of the cartridge, and it could be the whole EEPROM.
|
||||
* Read and send all the pages (64kiB), it should not impact the final PNG.
|
||||
*/
|
||||
for (int i = 0; i < 512; i++) {
|
||||
memset(pageData, 0, 128);
|
||||
// Arduino I2C buffer is 32 bytes, so we need multiple requests per page.
|
||||
for (int j = 0; j < 4; j++) {
|
||||
Wire.requestFrom(eeprom_addr, 32);
|
||||
for (int k = 0; k < 32; k++) {
|
||||
pageData[32*j + k] = Wire.read();
|
||||
}
|
||||
}
|
||||
// Try to send the full page until the recipient ACKs
|
||||
uint16_t page_checksum = sum_complement(pageData);
|
||||
do {
|
||||
memset(ret, 0, 4);
|
||||
size_t written = 0;
|
||||
while (written < 128) {
|
||||
written += Serial.write(pageData + written, min(128 - written, 64));
|
||||
}
|
||||
Serial.write((uint8_t*)&page_checksum, 2);
|
||||
while(!Serial.readBytesUntil('\n', ret, 3));
|
||||
Serial.read();
|
||||
} while(strncmp(ret, "ACK", 3) != 0);
|
||||
}
|
||||
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
// Handle the serial communication with the Arduino,
|
||||
// the cartridge transfer and related UI interactions.
|
||||
public class Arduino_Com : MonoBehaviour
|
||||
{
|
||||
private enum ArduinoState
|
||||
{
|
||||
Déconnecté,
|
||||
Connecté,
|
||||
Lecture,
|
||||
Écriture,
|
||||
Invalide
|
||||
}
|
||||
|
||||
// ======== UI ========
|
||||
[SerializeField] private UIDocument ui;
|
||||
private DropdownField _arduinoSerialPort;
|
||||
private EnumField _arduinoState;
|
||||
private Button _arduinoControlButton;
|
||||
private ProgressBar _arduinoProgress;
|
||||
|
||||
private Button _cartLoadButton;
|
||||
private Button _cartWriteButton;
|
||||
private VisualElement _cartImage;
|
||||
|
||||
// ======== IO ========
|
||||
private SerialPort _serial = new ();
|
||||
// Probably should be left alone but give the possibility to change it in the inspector.
|
||||
[SerializeField] private int baudrate = 115200;
|
||||
private string _activeSerialPort;
|
||||
private ArduinoState _state = ArduinoState.Déconnecté;
|
||||
|
||||
private string _cartPath;
|
||||
private FileStream _cart;
|
||||
|
||||
// ======== Cart transfer ========
|
||||
// Page data corresponding to the EEPROM.
|
||||
private const int PageSize = 128;
|
||||
private const int PageCount = 512;
|
||||
// Page to be written out.
|
||||
private Byte[] _pageBuffer = new byte[PageSize];
|
||||
// Current position in _pageBuffer.
|
||||
private int _readBytes;
|
||||
private int _currentPage;
|
||||
|
||||
// Checksum a 128 bytes page of data by summing it into an unsigned 16 bits int,
|
||||
// to be used either as a verification with a received checksum or to negate and
|
||||
// send as the checksum.
|
||||
private static UInt16 ComputeChecksum(Byte[] data)
|
||||
{
|
||||
if (data.Length != PageSize)
|
||||
{
|
||||
throw new InvalidDataException("Checksum only accepts "+PageSize+" bytes arrays !");
|
||||
}
|
||||
|
||||
UInt16 sum = 0;
|
||||
for (var i = 0; i < PageSize; i += 2)
|
||||
{
|
||||
sum += BitConverter.ToUInt16(data, i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private IEnumerator<WaitUntil> ReceiveSerialData()
|
||||
{
|
||||
do
|
||||
{
|
||||
yield return new WaitUntil(() => _serial.BytesToRead >= 1);
|
||||
// Read all available data, up to the PageSize.
|
||||
_readBytes += _serial.Read(_pageBuffer, _readBytes, PageSize - _readBytes);
|
||||
|
||||
// Full page received, check checksum and ACK/NAK, write received page.
|
||||
if (_readBytes >= 128)
|
||||
{
|
||||
Byte[] checksum = new byte[2];
|
||||
// There might be less than the two characters of the checksum available, loop until complete.
|
||||
var checksumRead = 0;
|
||||
do
|
||||
{
|
||||
checksumRead += _serial.Read(checksum, checksumRead, 2-checksumRead);
|
||||
} while (checksumRead < 2);
|
||||
|
||||
// Implicit type of the sum is 4 bytes, so the overflow doesn't occur. Compute manually.
|
||||
if (65536 - (BitConverter.ToUInt16(checksum) + ComputeChecksum(_pageBuffer)) == 0)
|
||||
{
|
||||
// Checksum valid, write it out to the cart.
|
||||
_cart.Write(_pageBuffer);
|
||||
_currentPage += 1;
|
||||
_serial.Write("ACK\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Checksum invalid, ask for retransmit.
|
||||
_serial.Write("NAK\n");
|
||||
Debug.LogWarningFormat("Page {0} reception failed, asking for retransmit.", _currentPage);
|
||||
}
|
||||
_readBytes = 0;
|
||||
}
|
||||
} while (_currentPage < PageCount);
|
||||
// We are done receiving the cart, we don't expect more data to be received so the coroutine can return.
|
||||
_currentPage = 0;
|
||||
_state = ArduinoState.Connecté;
|
||||
_cart.Close();
|
||||
|
||||
// Re-enable buttons now that the transfer is done.
|
||||
_arduinoControlButton.SetEnabled(true);
|
||||
_cartLoadButton.SetEnabled(true);
|
||||
// _cartWriteButton.SetEnabled(true); TODO: Not implemented
|
||||
|
||||
_arduinoProgress.visible = false;
|
||||
|
||||
// Update cart image !
|
||||
var newCartTexture = new Texture2D(2, 2);
|
||||
var textureData = File.ReadAllBytes(_cartPath);
|
||||
newCartTexture.LoadImage(textureData);
|
||||
_cartImage.style.backgroundImage = new StyleBackground(newCartTexture);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// ======== UI ========
|
||||
var uiRoot = ui.rootVisualElement;
|
||||
_arduinoSerialPort = uiRoot.Query<DropdownField>("Arduino-Serial");
|
||||
_arduinoSerialPort.RegisterValueChangedCallback(OnNewSerialPort);
|
||||
_arduinoSerialPort.choices = new List<string>(SerialPort.GetPortNames());
|
||||
|
||||
_arduinoState = uiRoot.Query<EnumField>("Arduino-State");
|
||||
|
||||
_arduinoControlButton = uiRoot.Query<Button>("Arduino-Button");
|
||||
_arduinoControlButton.clickable.clicked += OnArduinoButtonClicked;
|
||||
|
||||
_arduinoProgress = uiRoot.Query<ProgressBar>("Arduino-Progress");
|
||||
_arduinoProgress.highValue = PageCount;
|
||||
|
||||
_cartLoadButton = uiRoot.Query<Button>("Cart-Load");
|
||||
// Cart buttons can't work if the Arduino is not connected.
|
||||
_cartLoadButton.SetEnabled(false);
|
||||
_cartLoadButton.clickable.clicked += OnLoadButtonClicked;
|
||||
_cartWriteButton = uiRoot.Query<Button>("Cart-Write");
|
||||
_cartWriteButton.SetEnabled(false);
|
||||
_cartImage = uiRoot.Query<VisualElement>("Cart");
|
||||
|
||||
// ======== IO ========
|
||||
_cartPath = Application.temporaryCachePath + "/cart.png";
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Update the serial ports list so we can detect hot-plugs.
|
||||
_arduinoSerialPort.choices.Clear();
|
||||
_arduinoSerialPort.choices.AddRange(SerialPort.GetPortNames());
|
||||
|
||||
// Update state enum and progress bar if active.
|
||||
_arduinoState.value = _state;
|
||||
if (_state is ArduinoState.Lecture or ArduinoState.Écriture)
|
||||
_arduinoProgress.value = _currentPage;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_arduinoControlButton.clickable.clicked -= OnArduinoButtonClicked;
|
||||
_cartLoadButton.clickable.clicked -= OnLoadButtonClicked;
|
||||
_arduinoSerialPort.UnregisterValueChangedCallback(OnNewSerialPort);
|
||||
|
||||
if (_serial.IsOpen)
|
||||
_serial.Close();
|
||||
_cart?.Close();
|
||||
}
|
||||
|
||||
void OnLoadButtonClicked()
|
||||
{
|
||||
// Don't allow arduino actions during load.
|
||||
_arduinoControlButton.SetEnabled(false);
|
||||
_cartLoadButton.SetEnabled(false);
|
||||
_cartWriteButton.SetEnabled(false);
|
||||
|
||||
_arduinoProgress.visible = true;
|
||||
|
||||
// Clean up eventual previous cartridge.
|
||||
if (File.Exists(_cartPath))
|
||||
File.Delete(_cartPath);
|
||||
// Open the file, ready for transfers.
|
||||
_cart = File.OpenWrite(_cartPath);
|
||||
|
||||
// Start listening to incoming data asynchronously.
|
||||
StartCoroutine(ReceiveSerialData());
|
||||
_serial.Write("GO\n");
|
||||
|
||||
_state = ArduinoState.Lecture;
|
||||
}
|
||||
|
||||
void OnArduinoButtonClicked()
|
||||
{
|
||||
if (_state == ArduinoState.Connecté)
|
||||
{
|
||||
if (_serial.IsOpen)
|
||||
_serial.Close();
|
||||
_arduinoControlButton.text = "Connecter";
|
||||
|
||||
_cartLoadButton.SetEnabled(false);
|
||||
_cartWriteButton.SetEnabled(false);
|
||||
_arduinoSerialPort.SetEnabled(true);
|
||||
|
||||
_state = ArduinoState.Déconnecté;
|
||||
}
|
||||
else if (_state == ArduinoState.Déconnecté)
|
||||
{
|
||||
// Set baudrate again here in case it was changed in the inspector.
|
||||
_serial.BaudRate = baudrate;
|
||||
_serial.PortName = _activeSerialPort;
|
||||
_serial.Open();
|
||||
if (!_serial.IsOpen)
|
||||
{
|
||||
Debug.Log("Failed to connect to serial");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Wait ~2s for Arduino startup
|
||||
_arduinoControlButton.text = "Déconnecter";
|
||||
|
||||
_cartLoadButton.SetEnabled(true);
|
||||
// _cartWriteButton.SetEnabled(true); TODO: Not implemented
|
||||
_arduinoSerialPort.SetEnabled(false);
|
||||
|
||||
_state = ArduinoState.Connecté;
|
||||
}
|
||||
}
|
||||
|
||||
// The serial port should not be changeable when connected.
|
||||
void OnNewSerialPort(ChangeEvent<string> newPort)
|
||||
{
|
||||
if (Equals(newPort.newValue, _activeSerialPort))
|
||||
return;
|
||||
_activeSerialPort = newPort.newValue;
|
||||
|
||||
// Might be a slight race condition here if a port is removed in-between updates when clicked, so check anyway.
|
||||
_state = SerialPort.GetPortNames().Contains(newPort.newValue) ? ArduinoState.Déconnecté : ArduinoState.Invalide;
|
||||
_arduinoControlButton.SetEnabled(_state != ArduinoState.Invalide);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cc2a8d09604e9e8f5945bee6e08a173c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue