feat: Add HC-SR04 sensor support and enhance DHT22 response timing in simulation
parent
6a55f58e46
commit
083d8d69a8
|
|
@ -287,6 +287,29 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker)
|
|||
if sensor:
|
||||
sensor['responding'] = False
|
||||
|
||||
def _hcsr04_respond(trig_pin: int, echo_pin: int, distance_cm: float) -> None:
|
||||
"""Thread function: inject the HC-SR04 echo pulse via qemu_picsimlab_set_pin."""
|
||||
echo_slot = echo_pin + 1 # identity pinmap: slot = gpio + 1
|
||||
# Echo pulse width = distance_cm * 58 µs (speed of sound round trip)
|
||||
echo_us = max(100, int(distance_cm * 58))
|
||||
|
||||
try:
|
||||
# Wait for TRIG pulse to finish + propagation delay (~600 µs)
|
||||
_busy_wait_us(600)
|
||||
# Drive ECHO HIGH
|
||||
lib.qemu_picsimlab_set_pin(echo_slot, 1)
|
||||
# Hold ECHO HIGH for distance-proportional duration
|
||||
_busy_wait_us(echo_us)
|
||||
# Drive ECHO LOW
|
||||
lib.qemu_picsimlab_set_pin(echo_slot, 0)
|
||||
except Exception as exc:
|
||||
_log(f'HC-SR04 respond error on TRIG {trig_pin} ECHO {echo_pin}: {exc}')
|
||||
finally:
|
||||
with _sensors_lock:
|
||||
sensor = _sensors.get(trig_pin)
|
||||
if sensor:
|
||||
sensor['responding'] = False
|
||||
|
||||
# ── 5. ctypes callbacks (called from QEMU thread) ─────────────────────────
|
||||
|
||||
def _on_pin_change(slot: int, value: int) -> None:
|
||||
|
|
@ -298,7 +321,12 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker)
|
|||
# Sensor protocol dispatch by type
|
||||
with _sensors_lock:
|
||||
sensor = _sensors.get(gpio)
|
||||
if sensor is not None and sensor.get('type') == 'dht22':
|
||||
if sensor is None:
|
||||
return
|
||||
|
||||
stype = sensor.get('type', '')
|
||||
|
||||
if stype == 'dht22':
|
||||
if value == 0 and not sensor.get('responding', False):
|
||||
sensor['saw_low'] = True
|
||||
elif value == 1 and sensor.get('saw_low', False):
|
||||
|
|
@ -312,6 +340,19 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker)
|
|||
name=f'dht22-gpio{gpio}',
|
||||
).start()
|
||||
|
||||
elif stype == 'hc-sr04':
|
||||
# HC-SR04: detect TRIG going HIGH (firmware sends 10µs pulse)
|
||||
if value == 1 and not sensor.get('responding', False):
|
||||
sensor['responding'] = True
|
||||
echo_pin = int(sensor.get('echo_pin', gpio + 1))
|
||||
distance = float(sensor.get('distance', 40.0))
|
||||
threading.Thread(
|
||||
target=_hcsr04_respond,
|
||||
args=(gpio, echo_pin, distance),
|
||||
daemon=True,
|
||||
name=f'hcsr04-gpio{gpio}',
|
||||
).start()
|
||||
|
||||
def _on_dir_change(slot: int, direction: int) -> None:
|
||||
if _stopped.is_set():
|
||||
return
|
||||
|
|
|
|||
|
|
@ -71,7 +71,10 @@ function makeSimulator(adc?: ReturnType<typeof makeADC> | null) {
|
|||
pinManager,
|
||||
getADC: vi.fn().mockReturnValue(adc ?? null),
|
||||
setPinState: vi.fn(),
|
||||
cpu: { data: new Uint8Array(512).fill(0) },
|
||||
isRunning: vi.fn().mockReturnValue(true),
|
||||
getCurrentCycles: vi.fn().mockReturnValue(1000),
|
||||
getClockHz: vi.fn().mockReturnValue(16_000_000),
|
||||
cpu: { data: new Uint8Array(512).fill(0), cycles: 1000 },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2034,10 +2034,25 @@ void loop() {
|
|||
// Blinks the built-in LED (GPIO2) and an external LED (GPIO4)
|
||||
// Requires arduino-esp32 2.0.17 (IDF 4.4.x) — see docs/ESP32_EMULATION.md
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
#define LED_BUILTIN_PIN 2 // Built-in blue LED on ESP32 DevKit
|
||||
#define LED_EXT_PIN 4 // External red LED
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
pinMode(LED_BUILTIN_PIN, OUTPUT);
|
||||
pinMode(LED_EXT_PIN, OUTPUT);
|
||||
|
|
@ -2045,6 +2060,7 @@ void setup() {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
digitalWrite(LED_BUILTIN_PIN, HIGH);
|
||||
digitalWrite(LED_EXT_PIN, HIGH);
|
||||
Serial.println("LED ON");
|
||||
|
|
@ -2076,7 +2092,22 @@ void loop() {
|
|||
// Echoes anything received on Serial (UART0) back to the sender.
|
||||
// Open the Serial Monitor, type something, and see it echoed back.
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
delay(500);
|
||||
Serial.println("ESP32 Serial Echo ready!");
|
||||
|
|
@ -2084,6 +2115,7 @@ void setup() {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
if (Serial.available()) {
|
||||
String input = Serial.readStringUntil('\\n');
|
||||
input.trim();
|
||||
|
|
@ -2905,6 +2937,10 @@ void loop() {
|
|||
code: `// ESP32 — 7-Segment Display Counter 0-9
|
||||
// Segments: a=12, b=13, c=14, d=25, e=26, f=27, g=32
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
const int SEG[7] = {12, 13, 14, 25, 26, 27, 32};
|
||||
|
||||
const bool DIGITS[10][7] = {
|
||||
|
|
@ -2920,18 +2956,30 @@ const bool DIGITS[10][7] = {
|
|||
{1,1,1,1,0,1,1}, // 9
|
||||
};
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void showDigit(int d) {
|
||||
for (int i = 0; i < 7; i++)
|
||||
digitalWrite(SEG[i], DIGITS[d][i] ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
for (int i = 0; i < 7; i++) pinMode(SEG[i], OUTPUT);
|
||||
Serial.begin(115200);
|
||||
Serial.println("ESP32 7-Segment Counter");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
for (int d = 0; d <= 9; d++) {
|
||||
showDigit(d);
|
||||
Serial.print("Digit: "); Serial.println(d);
|
||||
|
|
@ -3770,10 +3818,25 @@ void loop() {
|
|||
code: `// ESP32 — HC-SR04 Ultrasonic Distance Sensor
|
||||
// Wiring: TRIG → D18 | ECHO → D19 | VCC → 3V3 | GND → GND
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
#define TRIG_PIN 18
|
||||
#define ECHO_PIN 19
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
pinMode(TRIG_PIN, OUTPUT);
|
||||
pinMode(ECHO_PIN, INPUT);
|
||||
|
|
@ -3791,6 +3854,7 @@ long measureCm() {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
long cm = measureCm();
|
||||
if (cm < 0) Serial.println("Out of range");
|
||||
else Serial.printf("Distance: %ld cm\\n", cm);
|
||||
|
|
@ -3818,13 +3882,27 @@ void loop() {
|
|||
// Requires: Adafruit MPU6050, Adafruit Unified Sensor libraries
|
||||
// Wiring: SDA → D21 | SCL → D22 | VCC → 3V3 | GND → GND
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
#include <Adafruit_MPU6050.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <Wire.h>
|
||||
|
||||
Adafruit_MPU6050 mpu;
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
Wire.begin(21, 22); // SDA=21, SCL=22
|
||||
if (!mpu.begin()) {
|
||||
|
|
@ -3838,6 +3916,7 @@ void setup() {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
sensors_event_t a, g, temp;
|
||||
mpu.getEvent(&a, &g, &temp);
|
||||
|
||||
|
|
@ -3869,13 +3948,28 @@ void loop() {
|
|||
code: `// ESP32 — PIR Motion Sensor
|
||||
// Wiring: OUT → D5 | VCC → 3V3 | GND → GND
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
#define PIR_PIN 5
|
||||
#define LED_PIN 2 // built-in blue LED on ESP32 DevKit
|
||||
|
||||
bool prevMotion = false;
|
||||
unsigned long detections = 0;
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
pinMode(PIR_PIN, INPUT);
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
|
|
@ -3885,6 +3979,7 @@ void setup() {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
bool motion = (digitalRead(PIR_PIN) == HIGH);
|
||||
if (motion && !prevMotion) {
|
||||
detections++;
|
||||
|
|
@ -3980,17 +4075,33 @@ void loop() {
|
|||
// Wiring: HORZ → D35 | VERT → D34 | SEL → D15
|
||||
// VCC → 3V3 | GND → GND
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
|
||||
#define JOY_HORZ 35 // input-only ADC pin
|
||||
#define JOY_VERT 34 // input-only ADC pin
|
||||
#define JOY_BTN 15 // GPIO with pull-up
|
||||
|
||||
void disableAllWDT() {
|
||||
esp_task_wdt_deinit();
|
||||
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_config0.en = 0;
|
||||
TIMERG0.wdt_wprotect = 0;
|
||||
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config0.en = 0;
|
||||
TIMERG1.wdt_wprotect = 0;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
disableAllWDT();
|
||||
Serial.begin(115200);
|
||||
pinMode(JOY_BTN, INPUT_PULLUP);
|
||||
Serial.println("ESP32 Joystick ready");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
disableAllWDT();
|
||||
int x = analogRead(JOY_HORZ); // 0–4095
|
||||
int y = analogRead(JOY_VERT); // 0–4095
|
||||
bool btn = (digitalRead(JOY_BTN) == LOW);
|
||||
|
|
|
|||
|
|
@ -414,17 +414,18 @@ function scheduleDHT22Response(simulator: any, pin: number, element: HTMLElement
|
|||
const payload = buildDHT22Payload(element);
|
||||
const now = simulator.getCurrentCycles() as number;
|
||||
|
||||
// Timing constants at 16 MHz (cycles per µs = 16)
|
||||
// DHT22 starts pulling the line LOW ~20 µs after MCU releases it HIGH.
|
||||
// The Adafruit DHT library v1.4.7 calls expectPulse(LOW) at ~55 µs (pullTime default),
|
||||
// so the preamble LOW must already be active by then. Starting at 20 µs (320 cycles)
|
||||
// guarantees the pin IS LOW when the library checks.
|
||||
const RESPONSE_START = 320; // 20 µs — DHT22 response start
|
||||
const LOW80 = 1280; // 80 µs LOW preamble
|
||||
const HIGH80 = 1280; // 80 µs HIGH preamble
|
||||
const LOW50 = 800; // 50 µs LOW marker before each bit
|
||||
const HIGH0 = 416; // 26 µs HIGH → bit '0'
|
||||
const HIGH1 = 1120; // 70 µs HIGH → bit '1'
|
||||
// Scale timing by CPU clock — AVR runs at 16 MHz, RP2040 at 125 MHz.
|
||||
const clockHz: number = typeof simulator.getClockHz === 'function'
|
||||
? simulator.getClockHz()
|
||||
: 16_000_000;
|
||||
const us = (microseconds: number) => Math.round(microseconds * clockHz / 1_000_000);
|
||||
|
||||
const RESPONSE_START = us(20); // DHT22 response start (~20 µs after MCU releases)
|
||||
const LOW80 = us(80); // 80 µs LOW preamble
|
||||
const HIGH80 = us(80); // 80 µs HIGH preamble
|
||||
const LOW50 = us(50); // 50 µs LOW marker before each bit
|
||||
const HIGH0 = us(26); // 26 µs HIGH → bit '0'
|
||||
const HIGH1 = us(70); // 70 µs HIGH → bit '1'
|
||||
|
||||
let t = now + RESPONSE_START;
|
||||
|
||||
|
|
@ -487,9 +488,13 @@ PartSimulationRegistry.register('dht22', {
|
|||
// Prevent DHT22's own scheduled pin changes from re-triggering the response.
|
||||
// After the MCU releases DATA HIGH and we begin responding, we ignore all
|
||||
// pin-change callbacks until the full waveform has been emitted.
|
||||
// DHT22 response is ~5 ms; at 16 MHz that is ~80 000 cycles. We gate for
|
||||
// 200 000 cycles (~12.5 ms) to give plenty of headroom.
|
||||
const RESPONSE_GATE_CYCLES = 200_000;
|
||||
// DHT22 response is ~5 ms; gate for ~12.5 ms scaled to the CPU clock.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const clockHz: number = typeof (simulator as any).getClockHz === 'function'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
? (simulator as any).getClockHz()
|
||||
: 16_000_000;
|
||||
const RESPONSE_GATE_CYCLES = Math.round(12_500 * clockHz / 1_000_000);
|
||||
let responseEndCycle = 0;
|
||||
let responseEndTimeMs = 0; // time-based fallback for ESP32 (no cycle counter)
|
||||
|
||||
|
|
|
|||
|
|
@ -631,18 +631,46 @@ PartSimulationRegistry.register('hc-sr04', {
|
|||
const echoPin = getArduinoPinHelper('ECHO');
|
||||
if (trigPin === null || echoPin === null) return () => {};
|
||||
|
||||
simulator.setPinState(echoPin, false); // ECHO LOW initially
|
||||
const el = element as any;
|
||||
let distanceCm = parseFloat(el.distance) || 10; // default distance in cm
|
||||
|
||||
let distanceCm = 10; // default distance in cm
|
||||
// ── ESP32 path: delegate protocol to backend QEMU worker ──
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const handledNatively = typeof (simulator as any).registerSensor === 'function'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
&& (simulator as any).registerSensor('hc-sr04', trigPin, {
|
||||
distance: distanceCm,
|
||||
echo_pin: echoPin,
|
||||
});
|
||||
|
||||
if (handledNatively) {
|
||||
registerSensorUpdate(componentId, (values) => {
|
||||
if ('distance' in values) {
|
||||
distanceCm = Math.max(2, Math.min(400, values.distance as number));
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(simulator as any).updateSensor(trigPin, {
|
||||
distance: distanceCm,
|
||||
echo_pin: echoPin,
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(simulator as any).unregisterSensor(trigPin);
|
||||
unregisterSensorUpdate(componentId);
|
||||
};
|
||||
}
|
||||
|
||||
// ── AVR / RP2040 path: local pin scheduling ──
|
||||
simulator.setPinState(echoPin, false); // ECHO LOW initially
|
||||
|
||||
const cleanup = simulator.pinManager.onPinChange(trigPin, (_: number, state: boolean) => {
|
||||
if (!state) return; // only react on TRIG HIGH
|
||||
// HC-SR04 timing (at 16 MHz):
|
||||
// - Sensor processing delay after TRIG: ~600 µs = 9600 cycles
|
||||
// - Echo duration = distanceCm / 17150 s × 16 000 000 cycles/s
|
||||
// (17150 cm/s = speed of sound, one-way = round-trip/2)
|
||||
if (typeof simulator.schedulePinChange === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const clockHz: number = typeof (simulator as any).getClockHz === 'function'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
? (simulator as any).getClockHz()
|
||||
: 16_000_000;
|
||||
const now = simulator.getCurrentCycles() as number;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ const SENSOR_COMPONENT_MAP: Record<string, {
|
|||
sensorType: string;
|
||||
dataPinName: string;
|
||||
propertyKeys: string[];
|
||||
extraPins?: Record<string, string>; // extra pin mappings: prop name → component pin name
|
||||
}> = {
|
||||
'dht22': { sensorType: 'dht22', dataPinName: 'SDA', propertyKeys: ['temperature', 'humidity'] },
|
||||
'hc-sr04': { sensorType: 'hc-sr04', dataPinName: 'TRIG', propertyKeys: ['distance'], extraPins: { echo_pin: 'ECHO' } },
|
||||
};
|
||||
|
||||
// ── Legacy type aliases (keep external consumers working) ──────────────────
|
||||
|
|
@ -590,6 +592,24 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
const val = comp.properties[key];
|
||||
if (val !== undefined) props[key] = typeof val === 'string' ? parseFloat(val) : val;
|
||||
}
|
||||
// Resolve extra pins (e.g. echo_pin for HC-SR04) from wires
|
||||
if (sensorDef.extraPins) {
|
||||
for (const [propName, compPinName] of Object.entries(sensorDef.extraPins)) {
|
||||
for (const ew of wires) {
|
||||
const epComp = (ew.start.componentId === comp.id && ew.start.pinName === compPinName)
|
||||
? ew.start : (ew.end.componentId === comp.id && ew.end.pinName === compPinName)
|
||||
? ew.end : null;
|
||||
if (!epComp) continue;
|
||||
const epBoard = epComp === ew.start ? ew.end : ew.start;
|
||||
if (!isBoardComponent(epBoard.componentId)) continue;
|
||||
const extraGpio = boardPinToNumber(board.boardKind, epBoard.pinName);
|
||||
if (extraGpio !== null && extraGpio >= 0) {
|
||||
props[propName] = extraGpio;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sensors.push(props);
|
||||
break; // only one data pin per sensor
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue